chore(org): Adapted org site to new prebuild structure
This commit is contained in:
parent
a5f0e00936
commit
acc72730df
8 changed files with 163 additions and 103 deletions
|
@ -19,36 +19,32 @@ export const ns = [navNs, 'docs']
|
|||
|
||||
const isEndSlug = (slug) => slug.split('/').length === 1
|
||||
|
||||
export const PostLayout = ({ children = [], slug, frontmatter, locale }) => {
|
||||
const { siteNav } = useNavigation({ ignoreControl: true })
|
||||
export const PostLayout = ({ children = [], slug, frontmatter, locale }) => (
|
||||
<>
|
||||
<FrontmatterHead {...{ frontmatter, slug, locale }} />
|
||||
<BaseLayout>
|
||||
<BaseLayoutLeft>
|
||||
<MainSections />
|
||||
<NavLinks />
|
||||
</BaseLayoutLeft>
|
||||
|
||||
return (
|
||||
<>
|
||||
<FrontmatterHead {...{ frontmatter, slug, locale }} />
|
||||
<BaseLayout>
|
||||
<BaseLayoutLeft>
|
||||
<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">
|
||||
<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>
|
||||
</BaseLayoutRight>
|
||||
</BaseLayout>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{children}
|
||||
<PrevNext noPrev={isEndSlug} noNext={isEndSlug} />
|
||||
</BaseLayoutProse>
|
||||
|
||||
<BaseLayoutRight>
|
||||
<div className="hidden xl:block">
|
||||
<Toc toc={frontmatter.toc} wrap />
|
||||
</div>
|
||||
</BaseLayoutRight>
|
||||
</BaseLayout>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
import { localePath } from 'shared/utils.mjs'
|
||||
const preGenerate = 6
|
||||
export const numPerPage = 12
|
||||
import { siteConfig as config } from 'site/site.config.mjs'
|
||||
|
||||
export const getPostSlugPaths = (order) => {
|
||||
export const getPostSlugPaths = (posts) => {
|
||||
const paths = []
|
||||
|
||||
for (const lang in order) {
|
||||
for (let i = 0; i < preGenerate; i++) {
|
||||
paths.push(localePath(lang, `${order[lang][i]}`))
|
||||
}
|
||||
for (const lang in posts) {
|
||||
paths.push(
|
||||
...Object.keys(posts[lang])
|
||||
.slice(0, config.posts.preGenerate)
|
||||
.map((slug) => localePath(lang, slug))
|
||||
)
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
export const getPostIndexPaths = (order, type) => {
|
||||
export const getPostIndexPaths = (posts, type) => {
|
||||
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/2`))
|
||||
}
|
||||
|
@ -24,13 +25,18 @@ export const getPostIndexPaths = (order, type) => {
|
|||
return paths
|
||||
}
|
||||
|
||||
export const getPostIndexProps = (locale, params, order, postInfo) => {
|
||||
const pageNum = parseInt(params.page)
|
||||
const numLocPages = Math.ceil(order[locale].length / numPerPage)
|
||||
export const getPostIndexProps = (pagenr, posts, meta) => {
|
||||
const pageNum = parseInt(pagenr)
|
||||
const numLocPages = Math.ceil(Object.keys(posts).length / config.posts.perPage)
|
||||
if (pageNum > numLocPages) return false
|
||||
|
||||
const postSlugs = order[locale].slice(numPerPage * (pageNum - 1), numPerPage * pageNum)
|
||||
const posts = postSlugs.map((s) => ({ ...postInfo[locale][s], s }))
|
||||
const pagePosts = Object.entries(posts)
|
||||
.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 }
|
||||
}
|
||||
|
|
|
@ -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 { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
import { useDynamicMdx } from 'shared/hooks/use-dynamic-mdx.mjs'
|
||||
|
@ -68,7 +69,7 @@ export async function getStaticProps({ params, locale }) {
|
|||
|
||||
export const getStaticPaths = async () => {
|
||||
return {
|
||||
paths: getPostSlugPaths(order),
|
||||
paths: getPostSlugPaths(posts),
|
||||
fallback: 'blocking',
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
// Dependencies
|
||||
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'
|
||||
// Hooks
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
@ -80,7 +81,7 @@ const BlogIndexPage = ({ posts, page, current, total }) => {
|
|||
<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">
|
||||
{posts.map((post) => (
|
||||
<Preview post={post} t={t} key={post.s} />
|
||||
<Preview post={post} t={t} key={post.slug} />
|
||||
))}
|
||||
</div>
|
||||
<Pagination {...{ current, total }} />
|
||||
|
@ -91,7 +92,7 @@ const BlogIndexPage = ({ posts, page, current, total }) => {
|
|||
export default BlogIndexPage
|
||||
|
||||
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 }
|
||||
|
||||
|
@ -110,7 +111,7 @@ export async function getStaticProps({ locale, params }) {
|
|||
|
||||
export const getStaticPaths = async () => {
|
||||
return {
|
||||
paths: getPostIndexPaths(order, 'blog'),
|
||||
paths: getPostIndexPaths(posts, 'blog'),
|
||||
fallback: 'blocking',
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,4 +20,8 @@ export const siteConfig = {
|
|||
languagesWip: [],
|
||||
site: 'FreeSewing.org',
|
||||
tld: 'org',
|
||||
posts: {
|
||||
preGenerate: 6,
|
||||
perPage: 12,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { exec } from 'node:child_process'
|
||||
import orderBy from 'lodash.orderby'
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
const loadFolderFrontmatter = async (key, site, folder, transform = false) => {
|
||||
const prefix = site === 'org' ? 'docs/' : ''
|
||||
const loadFolderFrontmatter = async (key, site, folder, transform = false, lang = false) => {
|
||||
const prefix = site === 'org' ? `${folder}/` : ''
|
||||
/*
|
||||
* Figure out what directory to spawn the child process in
|
||||
*/
|
||||
const cwd = await path.resolve(process.cwd(), '..', '..', 'markdown', site, folder)
|
||||
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) {
|
||||
console.error(`exec error: ${error}`)
|
||||
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}:'
|
||||
*/
|
||||
const chunks = match
|
||||
.split(`markdown/${site}/${site === 'org' ? 'docs/' : ''}`)
|
||||
.split(`markdown/${site}/${site === 'dev' ? '' : folder + '/'}`)
|
||||
.pop()
|
||||
.split(`.md:${key}:`)
|
||||
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
|
||||
*/
|
||||
const mergeOrder = (titles, order) => {
|
||||
const mergeOrder = (titles, order, withSlug = false) => {
|
||||
const pages = {}
|
||||
for (const lang in titles) {
|
||||
pages[lang] = {}
|
||||
for (const [slug, t] of Object.entries(titles[lang])) {
|
||||
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 folder = site === 'org' ? 'docs' : '.'
|
||||
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)
|
||||
}
|
||||
|
@ -131,19 +143,81 @@ const loadDocs = async (site) => {
|
|||
*/
|
||||
const loadBlog = async () => {
|
||||
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
|
||||
*/
|
||||
const loadShowcase = async () => {
|
||||
const titles = await loadFolderFrontmatter('title', 'org', 'blog')
|
||||
const order = await loadFolderFrontmatter('date', 'org', 'blog')
|
||||
const titles = await loadFolderFrontmatter('title', 'org', 'showcase')
|
||||
// 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 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)
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -192,9 +277,11 @@ export const prebuildPosts = async (store) => {
|
|||
store.posts = {
|
||||
blog: await loadBlog(),
|
||||
showcase: await loadShowcase(),
|
||||
newsletter: await loadNewsletter(),
|
||||
newsletter: { posts: await loadNewsletter() },
|
||||
}
|
||||
await writeFiles('blog', 'org', store.posts.blog)
|
||||
await writeFiles('showcase', 'org', store.posts.showcase)
|
||||
await writeFiles('blog', 'org', store.posts.blog.posts)
|
||||
await writeFiles('showcase', 'org', store.posts.showcase.posts)
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ export const prebuildNavigation = async (store) => {
|
|||
// Handle posts
|
||||
if (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 })
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue