fix(org): 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.
This commit is contained in:
parent
a4f192e090
commit
e2bf337bb2
4 changed files with 282 additions and 8 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -43,7 +43,6 @@ sites/lab/pages
|
||||||
!sites/lab/pages/*/index.js
|
!sites/lab/pages/*/index.js
|
||||||
|
|
||||||
# org auto-generated pages
|
# org auto-generated pages
|
||||||
sites/org/pages/docs/*
|
|
||||||
sites/org/pages/new/pattern/*
|
sites/org/pages/new/pattern/*
|
||||||
|
|
||||||
# Node dependencies
|
# Node dependencies
|
||||||
|
|
|
@ -17,10 +17,3 @@ developer/contributor docs, please refer to
|
||||||
|
|
||||||
</Tip>
|
</Tip>
|
||||||
|
|
||||||
<Fixme>
|
|
||||||
|
|
||||||
We should diataxis our docs
|
|
||||||
|
|
||||||
</Fixme>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
232
sites/org/pages/docs/[...slug].mjs
Normal file
232
sites/org/pages/docs/[...slug].mjs
Normal file
|
@ -0,0 +1,232 @@
|
||||||
|
// Used in static paths
|
||||||
|
import mdxPaths from 'site/prebuild/mdx.paths.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'
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PLEASE READ THIS BEFORE YOU TRY TO REFACTOR THIS PAGE
|
||||||
|
*
|
||||||
|
* You will notice that this page has a page component for each language
|
||||||
|
* and that those components are 95% identical. So you may be thinking:
|
||||||
|
*
|
||||||
|
* This is not DRY, let me refactor this real quick
|
||||||
|
*
|
||||||
|
* Before you do so, please reflect on these topics:
|
||||||
|
*
|
||||||
|
* - Do you know the pitfalls of dynamic imports in Webpack?
|
||||||
|
* - Do you know how much documentation we have?
|
||||||
|
* - Do you know we support 5 languages?
|
||||||
|
*
|
||||||
|
* If you do know all of these thigns, and you think you can improve this page. Go ahead.
|
||||||
|
*
|
||||||
|
* If you are not sure, then I would recommend you find something else to work on, unless
|
||||||
|
* you consider this a learning opportunity.
|
||||||
|
*
|
||||||
|
* joost
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const Loading = () => (
|
||||||
|
<Spinner className="w-24 h-24 color-primary animate-spin m-auto mt-8" />
|
||||||
|
)
|
||||||
|
|
||||||
|
export const HeadInfo = ({ frontmatter, locale, slug }) => (
|
||||||
|
<Head>
|
||||||
|
<meta property="og:title" content={frontmatter.title} key="title" />
|
||||||
|
<meta property="og:type" content="article" key="type" />
|
||||||
|
<meta property="og:description" content={``} key="type" />
|
||||||
|
<meta property="og:article:author" content="Joost De Cock" key="author" />
|
||||||
|
<meta
|
||||||
|
property="og:image"
|
||||||
|
content={`https://canary.backend.freesewing.org/og-img/en/org/${slug}}`}
|
||||||
|
key="image"
|
||||||
|
/>
|
||||||
|
<meta property="og:image:type" content="image/png" />
|
||||||
|
<meta property="og:image:width" content="1200" />
|
||||||
|
<meta property="og:image:height" content="630" />
|
||||||
|
<meta property="og:url" content={`https://freesewing.org/${slug}`} key="url" />
|
||||||
|
<meta property="og:locale" content="en" key="locale" />
|
||||||
|
<meta property="og:site_name" content="freesewing.org" key="site" />
|
||||||
|
<title>{frontmatter.title} - FreeSewing.org</title>
|
||||||
|
</Head>
|
||||||
|
)
|
||||||
|
|
||||||
|
export const Page = ({ page, frontmatter, slug, locale, MDX }) => (
|
||||||
|
<PageWrapper {...page} title={frontmatter.title}>
|
||||||
|
<HeadInfo {...{ frontmatter, locale, slug }} />
|
||||||
|
<div className="flex flex-row-reverse flex-wrap xl:flex-nowrap justify-end">
|
||||||
|
{false && frontmatter.toc && (
|
||||||
|
<div className="mb-8 w-full xl:w-80 2xl:w-96 xl:pl-8 2xl:pl-16">
|
||||||
|
{/* FIXME: Implement toc plugin to add it to the frontmatter */}
|
||||||
|
{/* <TocWrapper toc={frontmatter.toc} /> */}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<MdxWrapper>{MDX}</MdxWrapper>
|
||||||
|
</div>
|
||||||
|
</PageWrapper>
|
||||||
|
)
|
||||||
|
|
||||||
|
const EnDocsPage = ({ page, slug }) => {
|
||||||
|
// State
|
||||||
|
const [frontmatter, setFrontmatter] = useState({ title: 'FreeSewing.org' })
|
||||||
|
const [MDX, setMDX] = useState(<Loading />)
|
||||||
|
|
||||||
|
/* Load MDX dynamically */
|
||||||
|
useEffect(() => {
|
||||||
|
const loadMDX = async () => {
|
||||||
|
import(`../../../../markdown/org/${slug}/en.md`).then((mod) => {
|
||||||
|
setFrontmatter(mod.frontmatter)
|
||||||
|
const Component = mod.default
|
||||||
|
setMDX(<Component components={components} />)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
loadMDX()
|
||||||
|
}, [slug])
|
||||||
|
|
||||||
|
return <Page {...{ page, slug, frontmatter, MDX }} locale="en" />
|
||||||
|
}
|
||||||
|
|
||||||
|
const FrDocsPage = ({ page, slug }) => {
|
||||||
|
// State
|
||||||
|
const [frontmatter, setFrontmatter] = useState({ title: 'FreeSewing.org' })
|
||||||
|
const [MDX, setMDX] = useState(<Loading />)
|
||||||
|
|
||||||
|
/* Load MDX dynamically */
|
||||||
|
useEffect(() => {
|
||||||
|
const loadMDX = async () => {
|
||||||
|
import(`../../../../markdown/org/${slug}/fr.md`).then((mod) => {
|
||||||
|
setFrontmatter(mod.frontmatter)
|
||||||
|
const Component = mod.default
|
||||||
|
setMDX(<Component components={components} />)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
loadMDX()
|
||||||
|
}, [slug])
|
||||||
|
|
||||||
|
return <Page {...{ page, slug, frontmatter, MDX }} locale="fr" />
|
||||||
|
}
|
||||||
|
|
||||||
|
const EsDocsPage = ({ page, slug }) => {
|
||||||
|
// State
|
||||||
|
const [frontmatter, setFrontmatter] = useState({ title: 'FreeSewing.org' })
|
||||||
|
const [MDX, setMDX] = useState(<Loading />)
|
||||||
|
|
||||||
|
/* Load MDX dynamically */
|
||||||
|
useEffect(() => {
|
||||||
|
const loadMDX = async () => {
|
||||||
|
import(`../../../../markdown/org/${slug}/es.md`).then((mod) => {
|
||||||
|
setFrontmatter(mod.frontmatter)
|
||||||
|
const Component = mod.default
|
||||||
|
setMDX(<Component components={components} />)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
loadMDX()
|
||||||
|
}, [slug])
|
||||||
|
|
||||||
|
return <Page {...{ page, slug, frontmatter, MDX }} locale="es" />
|
||||||
|
}
|
||||||
|
|
||||||
|
const DeDocsPage = ({ page, slug }) => {
|
||||||
|
// State
|
||||||
|
const [frontmatter, setFrontmatter] = useState({ title: 'FreeSewing.org' })
|
||||||
|
const [MDX, setMDX] = useState(<Loading />)
|
||||||
|
|
||||||
|
/* Load MDX dynamically */
|
||||||
|
useEffect(() => {
|
||||||
|
const loadMDX = async () => {
|
||||||
|
import(`../../../../markdown/org/${slug}/de.md`).then((mod) => {
|
||||||
|
setFrontmatter(mod.frontmatter)
|
||||||
|
const Component = mod.default
|
||||||
|
setMDX(<Component components={components} />)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
loadMDX()
|
||||||
|
}, [slug])
|
||||||
|
|
||||||
|
return <Page {...{ page, slug, frontmatter, MDX }} locale="de" />
|
||||||
|
}
|
||||||
|
|
||||||
|
const NlDocsPage = ({ page, slug }) => {
|
||||||
|
// State
|
||||||
|
const [frontmatter, setFrontmatter] = useState({ title: 'FreeSewing.org' })
|
||||||
|
const [MDX, setMDX] = useState(<Loading />)
|
||||||
|
|
||||||
|
/* Load MDX dynamically */
|
||||||
|
useEffect(() => {
|
||||||
|
const loadMDX = async () => {
|
||||||
|
import(`../../../../markdown/org/${slug}/nl.md`).then((mod) => {
|
||||||
|
setFrontmatter(mod.frontmatter)
|
||||||
|
const Component = mod.default
|
||||||
|
setMDX(<Component components={components} />)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
loadMDX()
|
||||||
|
}, [slug])
|
||||||
|
|
||||||
|
return <Page {...{ page, slug, frontmatter, MDX }} locale="nl" />
|
||||||
|
}
|
||||||
|
|
||||||
|
const DocsPage = (props) => {
|
||||||
|
if (props.locale === 'en') return <EnDocsPage {...props} />
|
||||||
|
if (props.locale === 'fr') return <FrDocsPage {...props} />
|
||||||
|
if (props.locale === 'es') return <EsDocsPage {...props} />
|
||||||
|
if (props.locale === 'de') return <DeDocsPage {...props} />
|
||||||
|
if (props.locale === 'nl') return <NlDocsPage {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
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({ locale, params }) {
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
...(await serverSideTranslations('en', ['docs', ...ns])),
|
||||||
|
slug: 'docs/' + params.slug.join('/'),
|
||||||
|
locale,
|
||||||
|
page: {
|
||||||
|
locale,
|
||||||
|
path: ['docs', ...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() {
|
||||||
|
const somePaths = mdxPaths
|
||||||
|
.filter((path) => path.split('/').length < 5)
|
||||||
|
.filter((path) => path !== 'docs')
|
||||||
|
|
||||||
|
return {
|
||||||
|
paths: [
|
||||||
|
...somePaths.map((key) => `/${key}`),
|
||||||
|
...somePaths.map((key) => `/es/${key}`),
|
||||||
|
...somePaths.map((key) => `/de/${key}`),
|
||||||
|
...somePaths.map((key) => `/fr/${key}`),
|
||||||
|
...somePaths.map((key) => `/nl/${key}`),
|
||||||
|
],
|
||||||
|
fallback: 'blocking',
|
||||||
|
}
|
||||||
|
}
|
50
sites/org/pages/docs/index.mjs
Normal file
50
sites/org/pages/docs/index.mjs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
// Dependencies
|
||||||
|
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||||
|
// Hooks
|
||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
// Components
|
||||||
|
import { ns } from 'shared/components/wrappers/page.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'
|
||||||
|
import { Loading, HeadInfo, Page } from './[...slug].mjs'
|
||||||
|
|
||||||
|
const DocsHomePage = ({ page, slug, locale }) => {
|
||||||
|
// State
|
||||||
|
const [frontmatter, setFrontmatter] = useState({ title: 'FreeSewing.org' })
|
||||||
|
const [MDX, setMDX] = useState(<Loading />)
|
||||||
|
|
||||||
|
/* Load MDX dynamically */
|
||||||
|
useEffect(() => {
|
||||||
|
const loadMDX = async () => {
|
||||||
|
import(`../../../../markdown/org/docs/${locale}.md`).then((mod) => {
|
||||||
|
setFrontmatter(mod.frontmatter)
|
||||||
|
const Component = mod.default
|
||||||
|
setMDX(<Component components={components} />)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
loadMDX()
|
||||||
|
}, [slug])
|
||||||
|
|
||||||
|
return <Page {...{ page, slug, frontmatter, MDX, locale }} />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DocsHomePage
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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({ locale }) {
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
...(await serverSideTranslations('en', ['docs', ...ns])),
|
||||||
|
slug: 'docs',
|
||||||
|
locale,
|
||||||
|
page: {
|
||||||
|
locale,
|
||||||
|
path: ['docs'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue