[site/infra,#94][l]: switch from next/mdx to contentlayerfor rendering markdown files.
* Move all markdown into a dedicated `content/` directory * use contentlayer (https://github.com/contentlayerdev/contentlayer) for loading markdown * add catch all route pages/[...slug].js to generate all markdown routes using contentlayer to find pages frontmatter now properly supported NB: some nice side benefits like removing the hack in _app.js to render markdown template vs the general layout template TODO: page titles need fixing
This commit is contained in:
parent
fa710ef97f
commit
604708f165
|
|
@ -32,3 +32,6 @@ yarn-error.log*
|
||||||
|
|
||||||
# vercel
|
# vercel
|
||||||
.vercel
|
.vercel
|
||||||
|
|
||||||
|
# Contentlayer
|
||||||
|
.contentlayer
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,29 @@
|
||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { MDXProvider } from '@mdx-js/react'
|
|
||||||
|
|
||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
Head,
|
Head,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function MdxPage({ children }) {
|
export default function MdxPage({ children }) {
|
||||||
const { Component, pageProps } = children
|
const { Component, frontmatter } = children
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article className="prose dark:prose-invert mx-auto p-6">
|
<article className="prose dark:prose-invert mx-auto p-6">
|
||||||
<header>
|
<header>
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<h1>{pageProps.title}</h1>
|
<h1>{frontmatter.title}</h1>
|
||||||
{pageProps.author && (
|
{frontmatter.authors && (
|
||||||
<div className="-mt-6"><p className="opacity-60 pl-1">{pageProps.author}</p></div>
|
<div className="-mt-6"><p className="opacity-60 pl-1">{frontmatter.authors}</p></div>
|
||||||
)}
|
)}
|
||||||
{pageProps.description && (
|
{frontmatter.description && (
|
||||||
<p className="description">{pageProps.description}</p>
|
<p className="description">{frontmatter.description}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<section>
|
<main>
|
||||||
<MDXProvider components={components}>
|
<Component components={components} />
|
||||||
<Component {...pageProps} />
|
</main>
|
||||||
</MDXProvider>
|
|
||||||
</section>
|
|
||||||
</article>
|
</article>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { defineDocumentType, makeSource } from 'contentlayer/source-files'
|
||||||
|
// import readingTime from 'reading-time'
|
||||||
|
import remarkGfm from 'remark-gfm';
|
||||||
|
|
||||||
|
const OtherPage = defineDocumentType(() => ({
|
||||||
|
name: 'OtherPage',
|
||||||
|
filePathPattern: '**/*.md*',
|
||||||
|
contentType: 'mdx',
|
||||||
|
fields: {
|
||||||
|
title: { type: 'string', required: false },
|
||||||
|
date: { type: 'string', required: false },
|
||||||
|
authors: { type: 'string', required: false },
|
||||||
|
description: { type: 'string', required: false },
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default makeSource({
|
||||||
|
contentDirPath: 'content',
|
||||||
|
documentTypes: [OtherPage],
|
||||||
|
mdx: {
|
||||||
|
remarkPlugins: [remarkGfm],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -1,12 +1,16 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"contentlayer/generated": ["./.contentlayer/generated"]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"**/*.ts",
|
"**/*.ts",
|
||||||
"**/*.tsx",
|
"**/*.tsx",
|
||||||
"**/*.js",
|
"**/*.js",
|
||||||
"**/*.jsx",
|
"**/*.jsx",
|
||||||
|
".contentlayer/generated", "contentlayer.config.js",
|
||||||
],
|
],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|
@ -1,29 +1,10 @@
|
||||||
import gfm from 'remark-gfm'
|
|
||||||
import toc from 'remark-toc'
|
|
||||||
import slug from 'remark-slug'
|
|
||||||
import smartypants from '@silvenon/remark-smartypants'
|
|
||||||
|
|
||||||
import withMDXImp from '@next/mdx'
|
|
||||||
|
|
||||||
const withMDX = withMDXImp({
|
|
||||||
extension: /\.mdx?$/,
|
|
||||||
options: {
|
|
||||||
remarkPlugins: [gfm, toc, slug, smartypants],
|
|
||||||
rehypePlugins: [],
|
|
||||||
// If you use `MDXProvider`, uncomment the following line.
|
|
||||||
providerImportSource: "@mdx-js/react",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
// Append the default value with md extensions
|
|
||||||
pageExtensions: ['ts', 'tsx', 'js', 'jsx', 'md', 'mdx'],
|
|
||||||
webpack: (config, { buildId, dev }) => {
|
webpack: (config, { buildId, dev }) => {
|
||||||
config.resolve.symlinks = false
|
config.resolve.symlinks = false
|
||||||
return config
|
return config
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import { withContentlayer } from 'next-contentlayer'
|
||||||
|
|
||||||
export default withMDX(config)
|
export default withContentlayer()(config)
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -14,8 +14,10 @@
|
||||||
"@next/mdx": "^12.1.0",
|
"@next/mdx": "^12.1.0",
|
||||||
"@silvenon/remark-smartypants": "^1.0.0",
|
"@silvenon/remark-smartypants": "^1.0.0",
|
||||||
"@tailwindcss/typography": "^0.5.2",
|
"@tailwindcss/typography": "^0.5.2",
|
||||||
|
"contentlayer": "^0.1.2",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"next": "^12.1.0",
|
"next": "^12.1.0",
|
||||||
|
"next-contentlayer": "^0.1.2",
|
||||||
"next-seo": "^4.28.1",
|
"next-seo": "^4.28.1",
|
||||||
"next-themes": "^0.1.1",
|
"next-themes": "^0.1.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
import MdxPage from '../components/MDX';
|
||||||
|
import { allOtherPages } from 'contentlayer/generated';
|
||||||
|
import { useMDXComponent } from 'next-contentlayer/hooks';
|
||||||
|
import { NewsArticleJsonLd } from 'next-seo';
|
||||||
|
|
||||||
|
|
||||||
|
export default function Page({ body, ...rest }) {
|
||||||
|
const Component = useMDXComponent(body.code);
|
||||||
|
const children = {
|
||||||
|
Component,
|
||||||
|
frontmatter: {
|
||||||
|
authors: rest.authors,
|
||||||
|
title: rest.title,
|
||||||
|
date: rest.date,
|
||||||
|
description: rest.description,
|
||||||
|
modified: rest.modified,
|
||||||
|
tags: rest.tags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<MdxPage children={children} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getStaticProps = async ({ params }) => {
|
||||||
|
// All pages ending with .md in the /data folder are made available in allOtherPages
|
||||||
|
// Based on the specified slug, the correct page is selected
|
||||||
|
const urlPath = params.slug.join('/')
|
||||||
|
const page = allOtherPages.find(p => p._raw.flattenedPath === urlPath)
|
||||||
|
return { props: page }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getStaticPaths = async () => {
|
||||||
|
const paths = allOtherPages.map((page) => {
|
||||||
|
// demo => [demo]
|
||||||
|
// abc/demo => [abc,demo]
|
||||||
|
const parts = page._raw.flattenedPath.split('/')
|
||||||
|
return { params: { slug: parts } }
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
paths,
|
||||||
|
fallback: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -26,13 +26,14 @@ function MyApp({ Component, pageProps }) {
|
||||||
}
|
}
|
||||||
// end Google Analytics
|
// end Google Analytics
|
||||||
|
|
||||||
const pageTitle = (
|
// console.log(Component)
|
||||||
router.pathname == "/"
|
// const pageTitle = (
|
||||||
? "home"
|
// router.pathname == "/"
|
||||||
// convert slug to title
|
// ? "home"
|
||||||
: router.pathname.split("/").pop().replace(/-/g, " ")
|
// // convert slug to title
|
||||||
) // capitalize first char of each word
|
// : router.pathname.split("/").pop().replace(/-/g, " ")
|
||||||
.replace(/(^\w{1})|(\s{1}\w{1})/g, (str) => str.toUpperCase());
|
// ) // capitalize first char of each word
|
||||||
|
// .replace(/(^\w{1})|(\s{1}\w{1})/g, (str) => str.toUpperCase());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider attribute="class" defaultTheme="dark">
|
<ThemeProvider attribute="class" defaultTheme="dark">
|
||||||
|
|
@ -65,19 +66,11 @@ function MyApp({ Component, pageProps }) {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Layout title={pageTitle}>
|
<Layout title={""}>
|
||||||
{Component.layout == "js" ? (
|
<Component {...pageProps} />
|
||||||
<Component {...pageProps} />
|
|
||||||
) : (
|
|
||||||
<MdxPage children={{ Component, pageProps }} />
|
|
||||||
)}
|
|
||||||
</Layout>
|
</Layout>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if this is a markdown page use this layout by default ...
|
export default MyApp
|
||||||
// const MyLayout = pageProps.
|
|
||||||
|
|
||||||
|
|
||||||
export default MyApp
|
|
||||||
Loading…
Reference in New Issue