import path from 'path' import fs from 'fs' import axios from 'axios' import { strapiHost } from '../config/freesewing.mjs' import { unified } from 'unified' import remarkParser from 'remark-parse' import remarkCompiler from 'remark-stringify' import remarkFrontmatter from 'remark-frontmatter' import remarkFrontmatterExtractor from 'remark-extract-frontmatter' import yaml from 'yaml' import { remarkIntroPlugin } from '../mdx/remark-intro-plugin.mjs' import { writeFeed } from './feed.mjs' /* * Helper method to extract the intro from a Strapi post * * Parameters: * * - body: the post's body (markdown content) */ const postIntro = async markdown => await unified() .use(remarkIntroPlugin) .use(remarkParser) .use(remarkCompiler) .use(remarkFrontmatter) .use(remarkFrontmatterExtractor, { yaml: yaml.parse }) .process(markdown) /* * Helper method to create the API url for retrieval of Strapi posts */ const buildUrl = (type, site, lang) => (type === 'blog') ? `${strapiHost}/blogposts?_locale=${lang}&_sort=date:ASC&dev_${site === 'dev' ? 'eq' : 'ne'}=true` : `${strapiHost}/showcaseposts?_locale=${lang}&_sort=date:ASC` /* * Helper method to load posts from Strapi * Exported because it's re-used by the Algolia indexing script */ export const getPosts = async (type, site, lang) => { let res try { res = await axios.get(buildUrl(type, site, lang)) } catch (err) { console.log(`⚠️ Failed to load ${type} posts [${lang}]`) } const posts = {} for (const post of res?.data || []) { const intro = await postIntro(`---\n---\n\n${post.body}`) posts[post.slug] = { ...post, intro: intro.data.intro } } return posts } /* * Main method that does what needs doing */ export const prebuildStrapi = async (site) => { // Say hi console.log() console.log(`Prebuilding Strapi content for ${site}`) // What types of content to load const types = ['blog'] if (site === 'org') types.push('showcase') const posts = {} const authors = {} for (const type of types) { console.log(`${type}:`) posts[type] = {} authors[type] = {} // Loop over locales for (const lang of (site === 'dev' ? ['en'] : ['en', 'es', 'de', 'fr', 'nl'])) { authors[type][lang] = {} posts[type][lang] = {} console.log(` - Language: ${lang}`) posts[type][lang] = await getPosts(type, site, lang) // Extract list of authors for (const [slug, post] of Object.entries(posts[type][lang])) { if (type === 'blog') { if (!post.author?.id) console.log(post) authors[type][lang][post.author.slug] = post.author posts[type][lang][slug].author = post.author.slug } else { if (!post.maker?.id) console.log(post) authors[type][lang][post.maker.slug] = post.maker posts[type][lang][slug].maker = post.maker.slug } } // Write to disc, one file for the index page, one file for each post fs.writeFileSync( path.resolve('..', site, 'prebuild', `strapi.${type}.${lang}.js`), `export const posts = ${JSON.stringify(Object.values(posts[type][lang]).map(post => ({ title: post.title, date: post.date, slug: post.slug, author: post.author, img: post.image?.formats?.large?.url || 'https://posts.freesewing.org/uploads/logo_8401e711e4.png' })), null, 2)}` ) for (const [slug, post] of Object.entries(posts[type][lang])) { fs.writeFileSync( path.resolve('..', site, 'prebuild', `strapi.${type}.${lang}.${slug}.json`), JSON.stringify(post, null, 2) ) } // Write to disc, one file per author for (const [slug, author] of Object.entries(authors[type][lang])) { fs.writeFileSync( path.resolve('..', site, 'prebuild', `strapi.authors.${type}.${lang}.${slug}.json`), JSON.stringify({ slug: author.slug, name: author.name, displayname: author.displayname, about: author.about, img: author.picture?.formats?.medium?.url, }, null, 2) ) } // Create RSS/Atom/JSON feeds await writeFeed(site, type, lang, Object.values(posts[type][lang])) } } return [posts, authors] }