+
+
+
+
{frontmatter.title}
+
-
-
- >
- )
-}
+
+ {children}
+
+
+
+
+
+
+
+
+
+ >
+)
diff --git a/sites/org/components/mdx/posts/utils.mjs b/sites/org/components/mdx/posts/utils.mjs
index 5ef4c4a88c1..faa1a57af8c 100644
--- a/sites/org/components/mdx/posts/utils.mjs
+++ b/sites/org/components/mdx/posts/utils.mjs
@@ -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 }
}
diff --git a/sites/org/pages/blog/[slug].mjs b/sites/org/pages/blog/[slug].mjs
index 0aa095e42e0..72a7f94da23 100644
--- a/sites/org/pages/blog/[slug].mjs
+++ b/sites/org/pages/blog/[slug].mjs
@@ -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',
}
}
diff --git a/sites/org/pages/blog/index.mjs b/sites/org/pages/blog/index.mjs
deleted file mode 100644
index 00662c6073e..00000000000
--- a/sites/org/pages/blog/index.mjs
+++ /dev/null
@@ -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 (
-
- Implement blog view
-
- )
-}
-
-export default BlogIndexPage
-
-export async function getStaticProps({ locale }) {
- return {
- props: {
- ...(await serverSideTranslations(locale, namespaces)),
- slug: 'blog',
- page: {
- locale,
- path: ['blog'],
- },
- },
- }
-}
diff --git a/sites/org/pages/blog/page/[page].mjs b/sites/org/pages/blog/page/[page].mjs
index bc15271e869..fa57b7e9c62 100644
--- a/sites/org/pages/blog/page/[page].mjs
+++ b/sites/org/pages/blog/page/[page].mjs
@@ -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 }) => {
{posts.map((post) => (
-
+
))}
@@ -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',
}
}
diff --git a/sites/org/site.config.mjs b/sites/org/site.config.mjs
index 453896d2f4f..c1d0d415da4 100644
--- a/sites/org/site.config.mjs
+++ b/sites/org/site.config.mjs
@@ -20,4 +20,8 @@ export const siteConfig = {
languagesWip: [],
site: 'FreeSewing.org',
tld: 'org',
+ posts: {
+ preGenerate: 6,
+ perPage: 12,
+ },
}
diff --git a/sites/shared/prebuild/markdown.mjs b/sites/shared/prebuild/markdown.mjs
index e1e733a27b3..733b30a02cf 100644
--- a/sites/shared/prebuild/markdown.mjs
+++ b/sites/shared/prebuild/markdown.mjs
@@ -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)
}
diff --git a/sites/shared/prebuild/navigation.mjs b/sites/shared/prebuild/navigation.mjs
index 4ed0bec936f..14d3ec93898 100644
--- a/sites/shared/prebuild/navigation.mjs
+++ b/sites/shared/prebuild/navigation.mjs
@@ -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 })
}
}