1
0
Fork 0

feat(fs.dev): Added OG intro for description

This commit is contained in:
Joost De Cock 2021-12-28 20:01:59 +01:00
parent d71a8246ac
commit b59c5036ed
8 changed files with 51 additions and 150 deletions

View file

@ -1,45 +1,11 @@
/*
* This page will be used to generate all markdown content
* which, on freesewing.dev, is pretty much the entire website
* apart from the home page.
*
* It uses a dynamic route and data fetching to do that.
* More info is available at the bottom of this page, or check
* https://nextjs.org/docs/basic-features/data-fetching
*/
/*
* Page wrapper - a MUST for all pages
*/
import Page from 'shared/components/wrappers/page.js' import Page from 'shared/components/wrappers/page.js'
/*
* useApp hook - also a MUST for all pages
*/
import useApp from 'site/hooks/useApp.js' import useApp from 'site/hooks/useApp.js'
/*
* mdxMeta is generated in the prebuild step and contains
* meta-data about markdown content (titles, slugs, intro)
*/
import mdxMeta from 'site/prebuild/mdx.en.js' import mdxMeta from 'site/prebuild/mdx.en.js'
/*
* This loads MDX (markdown) from disk
*/
import mdxLoader from 'shared/mdx/loader' import mdxLoader from 'shared/mdx/loader'
/*
* This wraps MDX making sure to include all components
* like Tip, Note, Example, and so on
*/
import MdxWrapper from 'shared/components/wrappers/mdx' import MdxWrapper from 'shared/components/wrappers/mdx'
import Head from 'next/head'
import Popout from 'shared/components/popout.js' import Popout from 'shared/components/popout.js'
/*
* The NextJS page object
*/
const MdxPage = props => { const MdxPage = props => {
// This hook is used for shared code and global state // This hook is used for shared code and global state
@ -56,6 +22,15 @@ const MdxPage = props => {
*/ */
return ( return (
<Page app={app} {...props.page}> <Page app={app} {...props.page}>
<Head>
<meta property="og:title" content={props.page.title} key="title" />
<meta property="og:type" content="article" key='type' />
<meta property="og:description" content={props.intro} key='type' />
<meta property="og:article:author" content='Joost De Cock' key='author' />
<meta property="og:url" content={`https://canary.freesewing.dev/${props.page.slug}`} key='url' />
<meta property="og:locale" content="en_US" key='locale' />
<meta property="og:site_name" content="freesewing.dev" key='site' />
</Head>
<MdxWrapper mdx={props.mdx} app={app}/> <MdxWrapper mdx={props.mdx} app={app}/>
<Popout tip className='max-w-prose'> <Popout tip className='max-w-prose'>
<h6>Found a mistake?</h6> <h6>Found a mistake?</h6>
@ -87,11 +62,12 @@ export default MdxPage
*/ */
export async function getStaticProps({ params }) { export async function getStaticProps({ params }) {
const mdx = await mdxLoader('en', 'dev', params.mdxslug.join('/')) const { mdx, intro } = await mdxLoader('en', 'dev', params.mdxslug.join('/'))
return { return {
props: { props: {
mdx, mdx,
intro: intro.join(' '),
page: { page: {
slug: params.mdxslug.join('/'), slug: params.mdxslug.join('/'),
path: '/' + params.mdxslug.join('/'), path: '/' + params.mdxslug.join('/'),

View file

@ -49,17 +49,21 @@ const Author = ({ author }) => (
const PostPage = ({ post, author }) => { const PostPage = ({ post, author }) => {
const app = useApp() const app = useApp()
console.log(post)
return ( return (
<Page app={app} title={post.title}> <Page app={app} title={post.title}>
<Head> <Head>
<meta property="og:title" content={post.title} key="title" /> <meta property="og:title" content={post.title} key="title" />
<meta property="og:type" content="article" /> <meta property="og:type" content="article" key='type' />
<meta property="og:article:author" content={author.displayname} /> <meta property="og:description" content={post.intro || post.title} key='description' />
<meta property="og:url" content={`https://canary.freesewing.dev/blog/${post.slug}`} /> <meta property="og:article:author" content={author.displayname} key='author' />
<meta property="og:image" content={`${strapi}${post.image.formats.large.url}`} /> <meta property="og:url" content={`https://canary.freesewing.dev/blog/${post.slug}`} key='url' />
<meta property="og:local" content="en_US" /> <meta property="og:image" content={`${strapi}${post.image.formats.large.url}`} key='image' />
<meta property="og:site_name" content="freesewing.dev" /> <meta property="og:image:type" content={post.image.mime} key='image' />
<meta property="og:image:width" content={post.image.width} key='image' />
<meta property="og:image:height" content={post.image.height} key='image' />
<meta property="og:locale" content="en_US" key='locale' />
<meta property="og:site_name" content="freesewing.dev" key='site' />
</Head> </Head>
<article className="mb-12"> <article className="mb-12">
<div className="flex flex-row justify-between text-sm mb-1 mt-2"> <div className="flex flex-row justify-between text-sm mb-1 mt-2">

View file

@ -1,87 +0,0 @@
{
"dev": true,
"localizations": [],
"_id": "60f0616ddb32b45d5c4d6dac",
"body": "I wanted to do a quick write-up of the main layout blocks of our websites. So let's have a look:\n\n## Screen sizes and break points: 1024px wide or not?\n\nOur websites should work on a variety of devices:\n![screens.png](https://posts.freesewing.org/uploads/screens_cf77bf2abb.png)\n\nFor this, we practice so-called **responsive design**. A design that adapts to the screen size. \nOr rather, the screen width because thanks to scrolling, we always have plenty of height.\n\nIn practice, we use so-called **break points**. A list of one or more screen widths at which we apply a different layout for our website.\nTo keep things simple, we utilize a single breakpoint: **1024px**. \n\n - large screen: 1024 pixels or more wide\n - small screen: less than 1024 pixels wide\n\n## Page structure: Three elements, one dimension\n\nThe structure of each page on the website is a vertical column with three elements:\n\n - The header which holds the so-called `navbar`, short for *navigation bar*\n - The main part of the page, which in this case shows this blog post\n - The footer\n\nIt looks like this:\n\n![spage.png](https://posts.freesewing.org/uploads/spage_db9ce89cb5.png)\n\nThat's on large screens. We'll have a closer look at small screens later.\n\n## Footer\n\nThe footer is the simplest element. It's always there, and it sits at the bottom of the page.\n\nThe footer spans the entire width of the screen, but the content within is constrained by a maximum width. That width is **1536px** (indicated by the red dashed lines in the image above).\nThis is to avoid that people on a 4K monitor get neck cramps like they're attending some tennis match, just to read what's on the screen.\n\n## Navbar\n\nThe navbar is similar to the footer in the sense that it spans the full width, yet the content within is constrained to maximum 1536 pixels.\n\nOn large screens, the navbar sits at the top of the page and does not have a background color to make it stand out.\nThat's because it's rather intuitive to have the navigation at the top of the page.\n\nOn mobile, the navbar collapses gets fixed at the bottom of the screen, and gets a background color to draw attention to the fact that this is where the navigation controls are:\n\n![mpage.png](https://posts.freesewing.org/uploads/mpage_e3cade7d29.png)\n\nThe reason we place the navigation at the bottom of the screen is that it's easier to reach when using a mobile phone with one hand.\n\nRather than try to cram everything in the (now very small) navbar. We simply use a button that makes the whole screen the navigation menu on small screens.\n\n## Main layout\n\nThe *main* part of a page can be pretty much anything we want. But on most pages, it looks like this:\n\n![slayout.png](https://posts.freesewing.org/uploads/slayout_50f7b8270b.png)\n\nHere's too we are constraining the content to the same maximum width.\nApart from that, we have the main content of the page, and the `aside` which typically holds navigation.\n\nThis aside is not available on mobile, and instead can be reached by bringing up the menu.\n\n\n## Summary\n\nAll our websites look the same\n\n - The page is a vertical column made of 3 parts: header -> main -> footer\n - The main part can be any layout, but the most common has content + aside\n - On mobile, the navbar sits fixed at the bottom, and aside moves off-canvas\n\n",
"caption": " Photo by Kelly Lacy via Pexels",
"slug": "layout-blocks-overview",
"date": "2021-07-15",
"title": "A quick tour of the main layout blocks",
"linktitle": "Main layout blocks",
"published_at": "2021-07-15T16:25:20.922Z",
"createdAt": "2021-07-15T16:25:17.007Z",
"updatedAt": "2021-07-23T15:04:30.866Z",
"__v": 0,
"author": "joostdecock",
"image": {
"_id": "60f05ac3db32b45d5c4d6da7",
"name": "pexels-kelly-lacy-2402233.jpg",
"alternativeText": "",
"caption": "",
"hash": "pexels_kelly_lacy_2402233_45b5683839",
"ext": ".jpg",
"mime": "image/jpeg",
"size": 630.2,
"width": 1920,
"height": 1078,
"url": "/uploads/pexels_kelly_lacy_2402233_45b5683839.jpg",
"formats": {
"thumbnail": {
"name": "thumbnail_pexels-kelly-lacy-2402233.jpg",
"hash": "thumbnail_pexels_kelly_lacy_2402233_45b5683839",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 245,
"height": 138,
"size": 16.81,
"path": null,
"url": "/uploads/thumbnail_pexels_kelly_lacy_2402233_45b5683839.jpg"
},
"large": {
"name": "large_pexels-kelly-lacy-2402233.jpg",
"hash": "large_pexels_kelly_lacy_2402233_45b5683839",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 1000,
"height": 561,
"size": 207.61,
"path": null,
"url": "/uploads/large_pexels_kelly_lacy_2402233_45b5683839.jpg"
},
"medium": {
"name": "medium_pexels-kelly-lacy-2402233.jpg",
"hash": "medium_pexels_kelly_lacy_2402233_45b5683839",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 750,
"height": 421,
"size": 122.96,
"path": null,
"url": "/uploads/medium_pexels_kelly_lacy_2402233_45b5683839.jpg"
},
"small": {
"name": "small_pexels-kelly-lacy-2402233.jpg",
"hash": "small_pexels_kelly_lacy_2402233_45b5683839",
"ext": ".jpg",
"mime": "image/jpeg",
"width": 500,
"height": 281,
"size": 61.63,
"path": null,
"url": "/uploads/small_pexels_kelly_lacy_2402233_45b5683839.jpg"
}
},
"provider": "local",
"related": [
"60f0616ddb32b45d5c4d6dac"
],
"createdAt": "2021-07-15T15:56:51.570Z",
"updatedAt": "2021-07-15T16:25:17.017Z",
"__v": 0,
"id": "60f05ac3db32b45d5c4d6da7"
},
"site": "60ead51670e8d35a6d089fa0",
"locale": "en",
"id": "60f0616ddb32b45d5c4d6dac",
"intro": "I wanted to do a quick write-up of the main layout blocks of our websites. So let's have a look:"
}

View file

@ -108,13 +108,15 @@ const DefaultLayout = ({ app, title=false, children=[]}) => {
<PrimaryNavigation app={app} active={slug}/> <PrimaryNavigation app={app} active={slug}/>
</aside> </aside>
<section className='max-w-61.8% lg:pt-8 p-4 w-full'> <section className='max-w-61.8% lg:pt-8 p-4 w-full'>
{title && ( <div className="max-w-5xl">
<> {title && (
<Breadcrumbs app={app} slug={slug} title={title} /> <>
<PageTitle app={app} slug={slug} title={title} /> <Breadcrumbs app={app} slug={slug} title={title} />
</> <PageTitle app={app} slug={slug} title={title} />
)} </>
{children} )}
{children}
</div>
</section> </section>
</main> </main>
<Footer app={app} /> <Footer app={app} />

View file

@ -9,6 +9,7 @@ import { compile } from '@mdx-js/mdx'
import remarkFrontmatter from 'remark-frontmatter' import remarkFrontmatter from 'remark-frontmatter'
import remarkGfm from 'remark-gfm' import remarkGfm from 'remark-gfm'
import remarkCopyLinkedFiles from 'remark-copy-linked-files' import remarkCopyLinkedFiles from 'remark-copy-linked-files'
import { remarkIntroPlugin } from './remark-intro-plugin.mjs'
// Rehype plugins we want to use // Rehype plugins we want to use
import rehypeHighlight from 'rehype-highlight' import rehypeHighlight from 'rehype-highlight'
@ -29,6 +30,7 @@ const mdxLoader = async (language, site, slug) => {
path.resolve(`../../markdown/${site}/${slug}/${language}.md`), path.resolve(`../../markdown/${site}/${slug}/${language}.md`),
'utf-8' 'utf-8'
) )
const intro = []
const mdx = String( const mdx = String(
await compile(md, { await compile(md, {
outputFormat: 'function-body', outputFormat: 'function-body',
@ -42,6 +44,10 @@ const mdxLoader = async (language, site, slug) => {
sourceDir: path.resolve(`../../markdown/${site}/${slug}`), sourceDir: path.resolve(`../../markdown/${site}/${slug}`),
staticPath: '/mdx/', staticPath: '/mdx/',
} }
],
[
remarkIntroPlugin,
{ intro }
] ]
], ],
rehypePlugins: [ rehypePlugins: [
@ -50,7 +56,7 @@ const mdxLoader = async (language, site, slug) => {
}) })
) )
return mdx return {mdx, intro}
} }
export default mdxLoader export default mdxLoader

View file

@ -25,7 +25,9 @@ const asText = [
] ]
// The actual plugin starts here // The actual plugin starts here
export const remarkIntroPlugin = () => { export const remarkIntroPlugin = (opts={}) => {
const { intro=[] } = opts
// Check to see whether the node has frontmatter // Check to see whether the node has frontmatter
const hasFrontmatter = node => (node.children[0].type === 'yaml') const hasFrontmatter = node => (node.children[0].type === 'yaml')
@ -52,14 +54,15 @@ export const remarkIntroPlugin = () => {
// Tree visitor // Tree visitor
const visitor = (node) => { const visitor = (node) => {
const intro = extractFirstParagraph(node) const nodeIntro = extractFirstParagraph(node)
// Forgive me for this hack
intro.push(nodeIntro)
if (hasFrontmatter(node)) { if (hasFrontmatter(node)) {
node.children[0].value += `\nintro: "${intro}"\n` node.children[0].value += `\nintro: "${nodeIntro}"\n`
//console.log(node.children[0])
} else { } else {
node.children.unshift({ node.children.unshift({
type: 'yaml', type: 'yaml',
value: `intro: "${intro}"` value: `intro: "${nodeIntro}"`
}) })
} }
} }

View file

@ -11,7 +11,6 @@ import remarkMdx from 'remark-mdx'
import vfileReporter from 'vfile-reporter' import vfileReporter from 'vfile-reporter'
import { readSync } from 'to-vfile' import { readSync } from 'to-vfile'
import yaml from 'js-yaml' import yaml from 'js-yaml'
import { remarkIntroPlugin } from './remark-intro-plugin.mjs'
/* /*
@ -54,7 +53,7 @@ const fileToSlug = (file, site, lang) => (file.slice(-6) === `/${lang}.md`)
: false : false
/* /*
* Helper method to get the title and intro text from an MDX file * Helper method to get the title and meta data from an MDX file
* *
* Parameters: * Parameters:
* *
@ -64,8 +63,7 @@ const mdxMetaInfo = async file => {
let result let result
try { try {
result = await unified() result = await unified()
// .use(remarkMdx) //.use(remarkMdx)
// .use(remarkIntroPlugin)
.use(remarkParser) .use(remarkParser)
.use(remarkCompiler) .use(remarkCompiler)
.use(remarkFrontmatter) .use(remarkFrontmatter)
@ -109,7 +107,6 @@ export const prebuildMdx = async(site) => {
if (meta?.data?.title && meta?.data?.title) { if (meta?.data?.title && meta?.data?.title) {
pages[lang][slug] = { pages[lang][slug] = {
title: meta.data.title, title: meta.data.title,
intro: meta.data.intro,
order: meta.data.order order: meta.data.order
? meta.data.order+meta.data.title ? meta.data.order+meta.data.title
: meta.data.title, : meta.data.title,
@ -119,7 +116,7 @@ export const prebuildMdx = async(site) => {
: meta.data.title : meta.data.title
} }
} else { } else {
console.log('Failed to extract title & intro from:', slug) console.log('Failed to extract meta info from:', slug)
console.log(meta.messages, null ,2) console.log(meta.messages, null ,2)
} }
} }

View file

@ -8,7 +8,7 @@ import remarkCompiler from 'remark-stringify'
import remarkFrontmatter from 'remark-frontmatter' import remarkFrontmatter from 'remark-frontmatter'
import remarkFrontmatterExtractor from 'remark-extract-frontmatter' import remarkFrontmatterExtractor from 'remark-extract-frontmatter'
import yaml from 'yaml' import yaml from 'yaml'
import { remarkIntroPlugin } from './remark-intro-plugin.mjs' import { remarkIntroPlugin } from '../mdx/remark-intro-plugin.mjs'
/* /*