1
0
Fork 0

chore(org): Adapted org site to new prebuild structure

This commit is contained in:
Joost De Cock 2023-07-20 18:27:59 +02:00
parent a5f0e00936
commit acc72730df
8 changed files with 163 additions and 103 deletions

View file

@ -19,36 +19,32 @@ export const ns = [navNs, 'docs']
const isEndSlug = (slug) => slug.split('/').length === 1 const isEndSlug = (slug) => slug.split('/').length === 1
export const PostLayout = ({ children = [], slug, frontmatter, locale }) => { export const PostLayout = ({ children = [], slug, frontmatter, locale }) => (
const { siteNav } = useNavigation({ ignoreControl: true }) <>
<FrontmatterHead {...{ frontmatter, slug, locale }} />
<BaseLayout>
<BaseLayoutLeft>
<MainSections />
<NavLinks />
</BaseLayoutLeft>
return ( <BaseLayoutProse>
<> <div className="w-full">
<FrontmatterHead {...{ frontmatter, slug, locale }} /> <Breadcrumbs />
<BaseLayout> <h1 className="break-words searchme">{frontmatter.title}</h1>
<BaseLayoutLeft> <div className="block xl:hidden">
<MainSections />
<NavLinks />
</BaseLayoutLeft>
<BaseLayoutProse>
<div className="w-full">
<Breadcrumbs />
<h1 className="break-words searchme">{frontmatter.title}</h1>
<div className="block xl:hidden">
<Toc toc={frontmatter.toc} wrap />
</div>
</div>
{children}
<PrevNext noPrev={isEndSlug} noNext={isEndSlug} />
</BaseLayoutProse>
<BaseLayoutRight>
<div className="hidden xl:block">
<Toc toc={frontmatter.toc} wrap /> <Toc toc={frontmatter.toc} wrap />
</div> </div>
</BaseLayoutRight> </div>
</BaseLayout> {children}
</> <PrevNext noPrev={isEndSlug} noNext={isEndSlug} />
) </BaseLayoutProse>
}
<BaseLayoutRight>
<div className="hidden xl:block">
<Toc toc={frontmatter.toc} wrap />
</div>
</BaseLayoutRight>
</BaseLayout>
</>
)

View file

@ -1,22 +1,23 @@
import { localePath } from 'shared/utils.mjs' import { localePath } from 'shared/utils.mjs'
const preGenerate = 6 import { siteConfig as config } from 'site/site.config.mjs'
export const numPerPage = 12
export const getPostSlugPaths = (order) => { export const getPostSlugPaths = (posts) => {
const paths = [] const paths = []
for (const lang in order) { for (const lang in posts) {
for (let i = 0; i < preGenerate; i++) { paths.push(
paths.push(localePath(lang, `${order[lang][i]}`)) ...Object.keys(posts[lang])
} .slice(0, config.posts.preGenerate)
.map((slug) => localePath(lang, slug))
)
} }
return paths return paths
} }
export const getPostIndexPaths = (order, type) => { export const getPostIndexPaths = (posts, type) => {
const paths = [] const paths = []
for (const language in order) { for (const language in posts) {
paths.push(localePath(language, `${type}/page/1`)) paths.push(localePath(language, `${type}/page/1`))
paths.push(localePath(language, `${type}/page/2`)) paths.push(localePath(language, `${type}/page/2`))
} }
@ -24,13 +25,18 @@ export const getPostIndexPaths = (order, type) => {
return paths return paths
} }
export const getPostIndexProps = (locale, params, order, postInfo) => { export const getPostIndexProps = (pagenr, posts, meta) => {
const pageNum = parseInt(params.page) const pageNum = parseInt(pagenr)
const numLocPages = Math.ceil(order[locale].length / numPerPage) const numLocPages = Math.ceil(Object.keys(posts).length / config.posts.perPage)
if (pageNum > numLocPages) return false if (pageNum > numLocPages) return false
const postSlugs = order[locale].slice(numPerPage * (pageNum - 1), numPerPage * pageNum) const pagePosts = Object.entries(posts)
const posts = postSlugs.map((s) => ({ ...postInfo[locale][s], s })) .slice(config.posts.perPage * (pageNum - 1), config.posts.perPage * pageNum)
.map(([slug, post]) => ({
s: slug,
...post,
...meta[slug],
}))
return { posts, current: pageNum, total: numLocPages } return { posts: pagePosts, current: pageNum, total: numLocPages }
} }

View file

@ -1,4 +1,5 @@
import { order } from 'site/prebuild/blog-paths.mjs' import { pages as posts } from 'site/prebuild/blog.mjs'
import { meta } from 'site/prebuild/blog-meta.mjs'
import { getPostSlugPaths } from 'site/components/mdx/posts/utils.mjs' import { getPostSlugPaths } from 'site/components/mdx/posts/utils.mjs'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { useDynamicMdx } from 'shared/hooks/use-dynamic-mdx.mjs' import { useDynamicMdx } from 'shared/hooks/use-dynamic-mdx.mjs'
@ -68,7 +69,7 @@ export async function getStaticProps({ params, locale }) {
export const getStaticPaths = async () => { export const getStaticPaths = async () => {
return { return {
paths: getPostSlugPaths(order), paths: getPostSlugPaths(posts),
fallback: 'blocking', fallback: 'blocking',
} }
} }

View file

@ -1,35 +0,0 @@
// Dependencies
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
// Hooks
import { useTranslation } from 'next-i18next'
// Components
import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs'
import { Popout } from 'shared/components/popout.mjs'
// Translation namespaces used on this page
const namespaces = pageNs
const BlogIndexPage = ({ page, slug }) => {
const { t } = useTranslation()
return (
<PageWrapper {...page}>
<Popout fixme>Implement blog view</Popout>
</PageWrapper>
)
}
export default BlogIndexPage
export async function getStaticProps({ locale }) {
return {
props: {
...(await serverSideTranslations(locale, namespaces)),
slug: 'blog',
page: {
locale,
path: ['blog'],
},
},
}
}

View file

@ -1,6 +1,7 @@
// Dependencies // Dependencies
import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { postInfo, order } from 'site/prebuild/blog-paths.mjs' import { pages as posts } from 'site/prebuild/blog.mjs'
import { meta } from 'site/prebuild/blog-meta.mjs'
import { getPostIndexPaths, getPostIndexProps } from 'site/components/mdx/posts/utils.mjs' import { getPostIndexPaths, getPostIndexProps } from 'site/components/mdx/posts/utils.mjs'
// Hooks // Hooks
import { useTranslation } from 'next-i18next' import { useTranslation } from 'next-i18next'
@ -80,7 +81,7 @@ const BlogIndexPage = ({ posts, page, current, total }) => {
<PageWrapper {...page} t={t('sections:blog')}> <PageWrapper {...page} t={t('sections:blog')}>
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2 xl:grid-cols-3 max-w-7xl lg:pr-4 xl:pr-6"> <div className="grid grid-cols-1 gap-4 lg:grid-cols-2 xl:grid-cols-3 max-w-7xl lg:pr-4 xl:pr-6">
{posts.map((post) => ( {posts.map((post) => (
<Preview post={post} t={t} key={post.s} /> <Preview post={post} t={t} key={post.slug} />
))} ))}
</div> </div>
<Pagination {...{ current, total }} /> <Pagination {...{ current, total }} />
@ -91,7 +92,7 @@ const BlogIndexPage = ({ posts, page, current, total }) => {
export default BlogIndexPage export default BlogIndexPage
export async function getStaticProps({ locale, params }) { export async function getStaticProps({ locale, params }) {
const props = getPostIndexProps(locale, params, order, postInfo) const props = getPostIndexProps(params.page, posts[locale], meta)
if (props === false) return { notFound: true } if (props === false) return { notFound: true }
@ -110,7 +111,7 @@ export async function getStaticProps({ locale, params }) {
export const getStaticPaths = async () => { export const getStaticPaths = async () => {
return { return {
paths: getPostIndexPaths(order, 'blog'), paths: getPostIndexPaths(posts, 'blog'),
fallback: 'blocking', fallback: 'blocking',
} }
} }

View file

@ -20,4 +20,8 @@ export const siteConfig = {
languagesWip: [], languagesWip: [],
site: 'FreeSewing.org', site: 'FreeSewing.org',
tld: 'org', tld: 'org',
posts: {
preGenerate: 6,
perPage: 12,
},
} }

View file

@ -1,6 +1,7 @@
import fs from 'node:fs' import fs from 'node:fs'
import path from 'node:path' import path from 'node:path'
import { exec } from 'node:child_process' import { exec } from 'node:child_process'
import orderBy from 'lodash.orderby'
/* /*
* Shared header to include in written .mjs files * Shared header to include in written .mjs files
@ -25,14 +26,23 @@ const stripQuotes = (str) => {
/* /*
* This is the fast and low-tech way to some frontmatter from all files in a folder * This is the fast and low-tech way to some frontmatter from all files in a folder
*/ */
const loadFolderFrontmatter = async (key, site, folder, transform = false) => { const loadFolderFrontmatter = async (key, site, folder, transform = false, lang = false) => {
const prefix = site === 'org' ? 'docs/' : '' const prefix = site === 'org' ? `${folder}/` : ''
/* /*
* Figure out what directory to spawn the child process in * Figure out what directory to spawn the child process in
*/ */
const cwd = await path.resolve(process.cwd(), '..', '..', 'markdown', site, folder) const cwd = await path.resolve(process.cwd(), '..', '..', 'markdown', site, folder)
let list = false let list = false
const grep = exec('grep ^' + key + ': -RIsm 1 ' + cwd, { cwd }, (error, stdout, stderr) => { /*
* When going through a small number of files in a flat directory (eg. blog posts) a
* recursive grep through all files is faster.
* But the biggest task is combing through all the org documentation and for this
* it's much faster to first run find to limit the number of files to open
*/
const cmd = `find ${cwd} -type f -name "${
lang ? lang : '*'
}.md" -exec grep "^${key}:" -ism 1 {} +`
const grep = exec(cmd, { cwd }, (error, stdout, stderr) => {
if (error) { if (error) {
console.error(`exec error: ${error}`) console.error(`exec error: ${error}`)
return return
@ -61,7 +71,7 @@ const loadFolderFrontmatter = async (key, site, folder, transform = false) => {
* Trim some of the irrelevant path info prior to splitting on '.md:{key}:' * Trim some of the irrelevant path info prior to splitting on '.md:{key}:'
*/ */
const chunks = match const chunks = match
.split(`markdown/${site}/${site === 'org' ? 'docs/' : ''}`) .split(`markdown/${site}/${site === 'dev' ? '' : folder + '/'}`)
.pop() .pop()
.split(`.md:${key}:`) .split(`.md:${key}:`)
if (chunks.length === 2 && chunks[0].length > 1) { if (chunks.length === 2 && chunks[0].length > 1) {
@ -89,13 +99,14 @@ const loadFolderFrontmatter = async (key, site, folder, transform = false) => {
/* /*
* Merges in order key on those slugs that have it set * Merges in order key on those slugs that have it set
*/ */
const mergeOrder = (titles, order) => { const mergeOrder = (titles, order, withSlug = false) => {
const pages = {} const pages = {}
for (const lang in titles) { for (const lang in titles) {
pages[lang] = {} pages[lang] = {}
for (const [slug, t] of Object.entries(titles[lang])) { for (const [slug, t] of Object.entries(titles[lang])) {
pages[lang][slug] = { t } pages[lang][slug] = { t }
if (order[slug]) pages[lang][slug] = order[slug] if (order.en[slug]) pages[lang][slug].o = order.en[slug]
if (withSlug) pages[lang][slug].s = slug
} }
} }
@ -121,7 +132,8 @@ const formatDate = (date, slug, lang) => {
const loadDocs = async (site) => { const loadDocs = async (site) => {
const folder = site === 'org' ? 'docs' : '.' const folder = site === 'org' ? 'docs' : '.'
const titles = await loadFolderFrontmatter('title', site, folder) const titles = await loadFolderFrontmatter('title', site, folder)
const order = await loadFolderFrontmatter('order', site, folder) // Order is the same for all languages, so only grab EN files
const order = await loadFolderFrontmatter('order', site, folder, false, 'en')
return mergeOrder(titles, order) return mergeOrder(titles, order)
} }
@ -131,19 +143,81 @@ const loadDocs = async (site) => {
*/ */
const loadBlog = async () => { const loadBlog = async () => {
const titles = await loadFolderFrontmatter('title', 'org', 'blog') const titles = await loadFolderFrontmatter('title', 'org', 'blog')
const order = await loadFolderFrontmatter('date', 'org', 'blog', formatDate) // Order is the same for all languages, so only grab EN files
const order = await loadFolderFrontmatter('date', 'org', 'blog', formatDate, 'en')
// Author is the same for all languages, so only grab EN files
const authors = await loadFolderFrontmatter('author', 'org', 'blog', false, 'en')
// Image is the same for all languages, so only grab EN files
const images = await loadFolderFrontmatter('image', 'org', 'blog', false, 'en')
return mergeOrder(titles, order) // Merge titles and order for EN
const merged = {}
for (const slug in titles.en)
merged[slug] = {
t: titles.en[slug],
o: order.en[slug],
s: slug,
a: authors.en[slug],
i: images.en[slug],
}
// Order based on post data (descending)
const ordered = orderBy(merged, 'o', 'desc')
// Apply same order to all languages
const posts = {}
const meta = {}
const languages = Object.keys(titles).filter((lang) => lang !== 'em')
for (const lang of Object.keys(titles)) {
posts[lang] = {}
for (const post of ordered) {
posts[lang][post.s] = { t: post.t }
if (lang === 'en') meta[post.s] = { a: post.a, d: post.o, i: post.i }
}
}
return { posts, meta }
} }
/* /*
* Loads all showcase posts, titles and order * Loads all showcase posts, titles and order
*/ */
const loadShowcase = async () => { const loadShowcase = async () => {
const titles = await loadFolderFrontmatter('title', 'org', 'blog') const titles = await loadFolderFrontmatter('title', 'org', 'showcase')
const order = await loadFolderFrontmatter('date', 'org', 'blog') // Order is the same for all languages, so only grab EN files
const order = await loadFolderFrontmatter('date', 'org', 'showcase', formatDate, 'en')
// Author is the same for all languages, so only grab EN files
const makers = await loadFolderFrontmatter('maker', 'org', 'showcase', false, 'en')
// Image is the same for all languages, so only grab EN files
const images = await loadFolderFrontmatter('image', 'org', 'showcase', false, 'en')
return mergeOrder(titles, order) // Merge titles and order for EN
const merged = {}
for (const slug in titles.en)
merged[slug] = {
t: titles.en[slug],
o: order.en[slug],
s: slug,
m: makers.en[slug],
i: images.en[slug],
}
// Order based on post data (descending)
const ordered = orderBy(merged, 'o', 'desc')
// Apply same order to all languages
const posts = {}
const meta = {}
const languages = Object.keys(titles).filter((lang) => lang !== 'em')
for (const lang of Object.keys(titles)) {
posts[lang] = {}
for (const post of ordered) {
posts[lang][post.s] = { t: post.t }
if (lang === 'en') meta[post.s] = { m: post.m, d: post.o, i: post.i }
}
}
return { posts, meta }
} }
/* /*
@ -151,7 +225,8 @@ const loadShowcase = async () => {
*/ */
const loadNewsletter = async () => { const loadNewsletter = async () => {
const titles = await loadFolderFrontmatter('title', 'org', 'newsletter') const titles = await loadFolderFrontmatter('title', 'org', 'newsletter')
const order = await loadFolderFrontmatter('edition', 'org', 'newsletter') // Order is the same for all languages, so only grab EN files
const order = await loadFolderFrontmatter('edition', 'org', 'newsletter', false, 'en')
return mergeOrder(titles, order) return mergeOrder(titles, order)
} }
@ -177,6 +252,16 @@ export const pages = { ${Object.keys(pages).join(',')} }`
) )
} }
/*
* Write out a single prebuild file
*/
const writeFile = async (filename, exportname, site, content) => {
fs.writeFileSync(
path.resolve('..', site, 'prebuild', `${filename}.mjs`),
`${header}export const ${exportname} = ${JSON.stringify(content)}`
)
}
/* /*
* Main method that does what needs doing for the docs * Main method that does what needs doing for the docs
*/ */
@ -192,9 +277,11 @@ export const prebuildPosts = async (store) => {
store.posts = { store.posts = {
blog: await loadBlog(), blog: await loadBlog(),
showcase: await loadShowcase(), showcase: await loadShowcase(),
newsletter: await loadNewsletter(), newsletter: { posts: await loadNewsletter() },
} }
await writeFiles('blog', 'org', store.posts.blog) await writeFiles('blog', 'org', store.posts.blog.posts)
await writeFiles('showcase', 'org', store.posts.showcase) await writeFiles('showcase', 'org', store.posts.showcase.posts)
await writeFiles('newsletter', 'org', store.posts.newsletter) await writeFiles('newsletter', 'org', store.posts.newsletter)
await writeFile('blog-meta', 'meta', 'org', store.posts.blog.meta)
await writeFile('showcase-meta', 'meta', 'org', store.posts.showcase.meta)
} }

View file

@ -80,7 +80,7 @@ export const prebuildNavigation = async (store) => {
// Handle posts // Handle posts
if (posts) { if (posts) {
for (const type in posts) { for (const type in posts) {
for (const [slug, post] of Object.entries(posts[type][lang])) { for (const [slug, post] of Object.entries(posts[type].posts[lang])) {
set(sitenav, [lang, ...slug.split('/')], { t: post.t, o: post.o, s: slug }) set(sitenav, [lang, ...slug.split('/')], { t: post.t, o: post.o, s: slug })
} }
} }