|
| 1 | +import { notFound } from 'next/navigation'; |
| 2 | +import { unstable_setRequestLocale } from 'next-intl/server'; |
| 3 | +import type { FC } from 'react'; |
| 4 | + |
| 5 | +import { setClientContext } from '@/client-context'; |
| 6 | +import { MDXRenderer } from '@/components/mdxRenderer'; |
| 7 | +import { WithLayout } from '@/components/withLayout'; |
| 8 | +import { DEFAULT_VIEWPORT, ENABLE_STATIC_EXPORT } from '@/next.constants.mjs'; |
| 9 | +import { dynamicRouter } from '@/next.dynamic.mjs'; |
| 10 | +import { availableLocaleCodes, defaultLocale } from '@/next.locales.mjs'; |
| 11 | +import { MatterProvider } from '@/providers/matterProvider'; |
| 12 | + |
| 13 | +type DynamicStaticPaths = { path: string[]; locale: string }; |
| 14 | +type DynamicParams = { params: DynamicStaticPaths }; |
| 15 | + |
| 16 | +// This is the default Viewport Metadata |
| 17 | +// @see https://nextjs.org/docs/app/api-reference/functions/generate-viewport |
| 18 | +export const viewport = DEFAULT_VIEWPORT; |
| 19 | + |
| 20 | +// This generates each page's HTML Metadata |
| 21 | +// @see https://nextjs.org/docs/app/api-reference/functions/generate-metadata |
| 22 | +export const generateMetadata = async (c: DynamicParams) => { |
| 23 | + const { path = [], locale = defaultLocale.code } = c.params; |
| 24 | + |
| 25 | + const pathname = dynamicRouter.getPathname(path); |
| 26 | + |
| 27 | + // Retrieves and rewriting rule if the pathname matches any rule |
| 28 | + const [, rewriteRule] = dynamicRouter.getRouteRewrite(pathname); |
| 29 | + |
| 30 | + return dynamicRouter.getPageMetadata( |
| 31 | + locale, |
| 32 | + rewriteRule ? rewriteRule(pathname) : pathname |
| 33 | + ); |
| 34 | +}; |
| 35 | + |
| 36 | +// This provides all the possible paths that can be generated statically |
| 37 | +// + provides all the paths that we support on the Node.js Website |
| 38 | +export const generateStaticParams = async () => { |
| 39 | + const paths: DynamicStaticPaths[] = []; |
| 40 | + |
| 41 | + // We don't need to compute all possible paths on regular builds |
| 42 | + // as we benefit from Next.js's ISR (Incremental Static Regeneration) |
| 43 | + if (!ENABLE_STATIC_EXPORT) { |
| 44 | + return []; |
| 45 | + } |
| 46 | + |
| 47 | + for (const locale of availableLocaleCodes) { |
| 48 | + const routesForLanguage = await dynamicRouter.getRoutesByLanguage(locale); |
| 49 | + |
| 50 | + const mappedRoutesWithLocale = routesForLanguage.map(pathname => |
| 51 | + dynamicRouter.mapPathToRoute(locale, pathname) |
| 52 | + ); |
| 53 | + |
| 54 | + paths.push(...mappedRoutesWithLocale); |
| 55 | + } |
| 56 | + |
| 57 | + return paths.sort(); |
| 58 | +}; |
| 59 | + |
| 60 | +// This method parses the current pathname and does any sort of modifications needed on the route |
| 61 | +// then it proceeds to retrieve the Markdown file and parse the MDX Content into a React Component |
| 62 | +// finally it returns (if the locale and route are valid) the React Component with the relevant context |
| 63 | +// and attached context providers for rendering the current page |
| 64 | +const getPage: FC<DynamicParams> = async ({ params }) => { |
| 65 | + const { path = [], locale = defaultLocale.code } = params; |
| 66 | + |
| 67 | + if (!availableLocaleCodes.includes(locale)) { |
| 68 | + // Forces the current locale to be the Default Locale |
| 69 | + unstable_setRequestLocale(defaultLocale.code); |
| 70 | + |
| 71 | + return notFound(); |
| 72 | + } |
| 73 | + |
| 74 | + // Configures the current Locale to be the given Locale of the Request |
| 75 | + unstable_setRequestLocale(locale); |
| 76 | + |
| 77 | + const pathname = dynamicRouter.getPathname(path); |
| 78 | + |
| 79 | + if (dynamicRouter.shouldIgnoreRoute(pathname)) { |
| 80 | + return notFound(); |
| 81 | + } |
| 82 | + |
| 83 | + // Retrieves and rewriting rule if the pathname matches any rule |
| 84 | + const [, rewriteRule] = dynamicRouter.getRouteRewrite(pathname); |
| 85 | + |
| 86 | + // We retrieve the source of the Markdown file by doing an educated guess |
| 87 | + // of what possible files could be the source of the page, since the extension |
| 88 | + // context is lost from `getStaticProps` as a limitation of Next.js itself |
| 89 | + const { source, filename } = await dynamicRouter.getMarkdownFile( |
| 90 | + locale, |
| 91 | + rewriteRule ? rewriteRule(pathname) : pathname |
| 92 | + ); |
| 93 | + |
| 94 | + if (source.length && filename.length) { |
| 95 | + // This parses the source Markdown content and returns a React Component and |
| 96 | + // relevant context from the Markdown File |
| 97 | + const { MDXContent, frontmatter, headings } = |
| 98 | + await dynamicRouter.getMDXContent(source, filename); |
| 99 | + |
| 100 | + // Defines a shared Server Context for the Client-Side |
| 101 | + // That is shared for all pages under the dynamic router |
| 102 | + setClientContext({ frontmatter, headings, pathname }); |
| 103 | + |
| 104 | + return ( |
| 105 | + <MatterProvider matter={frontmatter} headings={headings}> |
| 106 | + <WithLayout layout={frontmatter.layout}> |
| 107 | + <MDXRenderer Component={MDXContent} /> |
| 108 | + </WithLayout> |
| 109 | + </MatterProvider> |
| 110 | + ); |
| 111 | + } |
| 112 | + |
| 113 | + return notFound(); |
| 114 | +}; |
| 115 | + |
| 116 | +// Enforce that all these routes are compatible with SSR |
| 117 | +export const dynamic = 'error'; |
| 118 | + |
| 119 | +export default getPage; |
0 commit comments