1
0
Fork 0

feat(shared): Add git info to prebuild step

This commit is contained in:
Joost De Cock 2023-05-17 10:53:41 +02:00
parent c047b905df
commit 5b12ea49ec
5 changed files with 156 additions and 182 deletions

View file

@ -1,6 +1,48 @@
/*
* A list of (documentation) authors.
* The ID is the (future v3) FreeSewing user ID (zero for now)
* The name is what we'll use as display name
*/
export const authors = {
joostdecock: {
id: 1,
name: 'Joost De Cock',
},
joostdecock: { id: 0, name: 'Joost De Cock' },
benjamesben: { id: 0, name: 'Benjamin' },
nikhil: { id: 0, name: 'nikhil' },
jackseye: { id: 0, name: 'jackseye' },
'Annie Kao': { id: 0, name: 'Annie Kao' },
Bart: { id: 0, name: 'Bart' },
'Enoch Riese': { id: 0, name: 'Enoch Riese' },
Zee: { id: 0, name: 'Zee' },
'James Bradbury': { id: 0, name: 'James Bradbury' },
jgfichte: { id: 0, name: 'jgfichte' },
Tríona: { id: 0, name: 'Tríona' },
starfetch: { id: 0, name: 'starfetch' },
bobgeorgethe3rd: { id: 0, name: 'starfetch' },
'Glenn Matthews': { id: 0, name: 'Glenn Matthews' },
'Raphael Sizemore': { id: 0, name: 'Raphael Sizemore' },
'Joe Schofield': { id: 0, name: 'Joe Schofield' },
mergerg: { id: 0, name: 'starfetch' },
woutervdub: { id: 0, name: 'Wouter van Wageningen' },
'anna-puk': { id: 0, name: 'Anna Puk' },
'Nick Dower': { id: 0, name: 'Nick Dower' },
'Sanne Kalkman': { id: 0, name: 'Sanne Kalkman' },
'Darigov Research': { id: 0, name: 'Darigov Research' },
'Jeroen Hoek': { id: 0, name: 'Jeroen Hoek' },
Natalia: { id: 0, name: 'Natalia Sayang' },
chri5b: { id: 0, name: 'chri5b' },
tangerineshark: { id: 0, name: 'tangerineshark' },
'MA-TATAS': { id: 0, name: 'MA-TATAS' },
}
/*
* Maps git commiter name to author for those cases where the
* name in the authors table is different
*/
export const gitToAuthor = {
'Joost De Cock': 'joostdecock',
'Benjamin F': 'benjamesben',
SeaZeeZee: 'Zee',
'Wouter van Wageningen': 'woutervdub',
'bobgeorgethe3rd@googlemail.com': 'bobgeorgethe3rd',
'70777269+tangerineshark@users.noreply.github.com': 'tangerineshark',
'thijs.assies@gmail.com': 'MA-TATAS',
}

View file

@ -2,6 +2,7 @@
import { components as baseComponents } from 'shared/components/mdx/index.mjs'
// List of authors
import { authors as allAuthors } from 'config/authors.mjs'
import { docUpdates } from 'site/prebuild/doc-updates.mjs'
// FreeSewing config
import { freeSewingConfig } from 'shared/config/freesewing.config.mjs'
// Components
@ -55,7 +56,7 @@ const Ul = ({ children }) => (
)
const MetaData = ({ authors = [], maintainers = [], updated = '20220825', locale, slug, t }) => (
<div className="py-4 px-4 rounded-lg bg-secondary bg-opacity-5 shadow mb-4">
<div className="py-4 px-4 rounded-lg bg-secondary bg-opacity-20 shadow mb-4">
{locale === 'en' ? (
<div className="flex flex-row items-center gap-4 justify-between text-base-content mb-2">
<span className="font-medium text-lg">{t('helpImproveDocs')}</span>
@ -84,7 +85,7 @@ const MetaData = ({ authors = [], maintainers = [], updated = '20220825', locale
{authors.length > 0 ? (
<>
<li className="list-none font-medium opacity-70 italic">{t('authors')}:</li>
<PersonList list={authors} />
<PersonList list={authors} slug={slug} />
</>
) : null}
@ -109,11 +110,16 @@ export const MdxWrapper = ({ MDX, frontmatter = {}, components = {} }) => {
const allComponents = { ...baseComponents, ...components }
const { locale, slug } = useContext(NavigationContext)
const { authors = [], maintainers = [] } = frontmatter || {}
const updates = docUpdates[slug] || {}
return (
<div className="text-primary mdx max-w-prose text-base-content max-w-prose text-base">
<MetaData {...frontmatter} {...{ locale, slug, t }} />
<MetaData
maintainers={frontmatter?.maintainers || []}
authors={updates?.authors || []}
lastUpdated={updates?.lastUpdates}
{...{ locale, slug, t }}
/>
<MDX components={allComponents} />
</div>
)

View file

@ -0,0 +1,94 @@
import execa from 'execa'
import { gitToAuthor, authors as authorInfo } from '../../../config/authors.mjs'
import path from 'path'
import fs from 'fs'
import rdir from 'recursive-readdir'
import { getMdxFileList, fileToSlug } from './docs.mjs'
const divider = '____'
const parseLog = (line) => line.split(divider).map((item) => item.trim())
/*
* Extracts git authors and last modification date from git log.
* Strictly speaking, it's the last commit date, but you get the idea.
*/
export const getGitMetadata = async (file, site) => {
const slug = fileToSlug(file, site, 'en')
const log = await execa.command(
`git log --pretty="format:%cs${divider}%aN${divider}%aE" ${file}`,
{ shell: true }
)
const authors = new Set()
let lastUpdated = false
for (const line of log.stdout.split('\n')) {
const [date, author, email] = parseLog(line)
if (!lastUpdated) lastUpdated = date.split('-').join('')
let key = false
if (typeof authorInfo[author] !== 'undefined') key = author
else {
if (typeof gitToAuthor[author] !== 'undefined') {
key = gitToAuthor[author]
} else if (typeof gitToAuthor[email] !== 'undefined') {
key = gitToAuthor[email]
}
}
if (!key) throw `Git author email ${email} is unknown in the git-to-author table`
else authors.add(key)
}
return {
lastUpdated,
authors,
slug,
}
}
/*
* Main method that does what needs doing
*/
export const prebuildGitData = async (site) => {
// Say hi
console.log()
console.log(`Prebuilding git author data for freesewing.${site}`)
// Setup MDX root path
const root = ['..', '..', 'markdown', site]
if (site === 'org') root.push('docs')
const mdxRoot = path.resolve(...root)
const pages = {}
// Get list of filenames
const list = await getMdxFileList(mdxRoot, 'en')
// Loop over files
for (const file of list) {
const { lastUpdated, authors, slug } = await getGitMetadata(file, site)
pages[slug] = { lastUpdated, authors: [...authors] }
}
// Write page to disk
const dir = path.resolve('..', site, 'prebuild')
fs.mkdirSync(dir, { recursive: true })
fs.writeFileSync(
path.resolve(dir, `doc-updates.mjs`),
`export const docUpdates = ${JSON.stringify(pages)}`
)
// How about some stats
const stats = {}
for (const slug in pages) {
for (const author of pages[slug].authors) {
if (typeof stats[author] === 'undefined') stats[author] = 0
stats[author]++
}
}
fs.writeFileSync(
path.resolve(dir, `doc-stats.mjs`),
`export const docStats = ${JSON.stringify(stats, null, 2)}`
)
return pages
}

View file

@ -1,6 +1,6 @@
import { prebuildDocs } from './docs.mjs'
//import { prebuildMdx } from './mdx.mjs'
import { prebuildNavigation } from './navigation.mjs'
import { prebuildGitData } from './git.mjs'
import { prebuildContributors } from './contributors.mjs'
import { prebuildPatrons } from './patrons.mjs'
import { prebuildI18n } from './i18n.mjs'
@ -10,13 +10,12 @@ import { generateOgImage } from './og/index.mjs'
const run = async () => {
const SITE = process.env.SITE || 'lab'
if (SITE === 'org') {
prebuildDesigns()
const docPages = await prebuildDocs(SITE)
const posts = {}
prebuildNavigation(docPages, posts, SITE)
} else if (SITE === 'dev') {
let docPages
if (['org', 'dev'].includes(SITE)) {
await prebuildGitData(SITE)
const docPages = await prebuildDocs(SITE)
prebuildNavigation(docPages, false, SITE)
if (SITE === 'org') prebuildDesigns()
if (process.env.GENERATE_OG_IMAGES) {
// Create og image for the home page
await generateOgImage({
@ -35,7 +34,6 @@ const run = async () => {
lead: '404',
})
}
prebuildNavigation(docPages, false, SITE)
} else await prebuildLab()
await prebuildI18n(SITE)

View file

@ -1,166 +0,0 @@
import path from 'path'
import fs from 'fs'
import rdir from 'recursive-readdir'
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 { readSync } from 'to-vfile'
import yaml from 'js-yaml'
import { mdIntro } from './md-intro.mjs'
import { generateOgImage } from './og/index.mjs'
/*
* There's an issue in crowdin where it changes the frontmatter marker:
* ---
* into this:
* - - -
* which breaks stuff. So this method takes the input and replaces all
* - - - with ---
*/
export const fixCrowdinBugs = (md) => {
md.value = md.value.split('- - -\n').join('---\n')
return md
}
/*
* Helper method to get a list of MDX files in a folder.
* Will traverse recursively to get all files from a given root folder.
*
* Parameters:
*
* - folder: the root folder to look in
* - lang: the language files to looks for
*
* Exported because it's also used by the Algolia index script
*/
export const getMdxFileList = async (folder, lang) => {
let allFiles
try {
allFiles = await rdir(folder)
} catch (err) {
console.log(err)
return false
}
// Filter out all that's not a language-specific markdown file
// and avoid including the 'ui' files
const files = []
for (const file of allFiles) {
if (
file.slice(-5) === `${lang}.md` &&
file.indexOf('/ui/') === -1 &&
file.indexOf('/uimd/') === -1
)
files.push(file)
}
return files.sort()
}
/*
* Helper method to get the website slug (path) from the file path
*/
export const fileToSlug = (file, site, lang) =>
file.slice(-6) === `/${lang}.md` ? file.split(`/markdown/${site}/`).pop().slice(0, -6) : false
/*
* Helper method to get the title and meta data from an MDX file
*
* Parameters:
*
* - file: the full path to the file
*/
const mdxMetaInfo = async (file) => {
let result
try {
result = await unified()
.use(remarkParser)
.use(remarkCompiler)
.use(remarkFrontmatter)
.use(remarkFrontmatterExtractor, { yaml: yaml.load })
.process(fixCrowdinBugs(readSync(file, { encoding: 'utf-8' })))
} catch (err) {
console.log(err)
}
return result
}
/*
* Main method that does what needs doing
*/
export const prebuildMdx = async (site) => {
// Say hi
console.log()
console.log(`Prebuilding MDX for freesewing.${site}`)
// Setup MDX root path
const mdxRoot = path.resolve('..', '..', 'markdown', site)
// Inform if we're also generating OG images
if (process.env.GENERATE_OG_IMAGES) {
console.log('⚙ Also generating Open Graph images (this takes a while)')
console.log(' Unset the GENERATE_OG_IMAGES env var to skip this step)')
} else {
console.log('⏩ Not generating Open Graph images as it takes a while')
console.log(' Set GENERATE_OG_IMAGES env var to include this step')
}
// Loop over locales
const pages = {}
const locales = site === 'dev' ? ['en'] : ['en', 'fr', 'es', 'nl', 'de']
for (const lang of locales) {
console.log(` - Language: ${lang}`)
// Get list of filenames
const list = await getMdxFileList(mdxRoot, lang)
// Parse them for title and intro
pages[lang] = {}
for (const file of list) {
const slug = fileToSlug(file, site, lang)
if (slug) {
const meta = await mdxMetaInfo(file)
if (meta.data?.title) {
pages[lang][slug] = { t: meta.data.title }
if (meta.data.order) pages[lang][slug].o = `${meta.data.order}${meta.data.title}`
} else {
if (pages.en[slug]) {
console.log(`l Falling back to EN metadata for ${slug}`)
pages[lang][slug] = pages.en[slug]
} else {
console.log(`❌ [${lang}] Failed to extract meta info from: ${slug}`)
if (meta.messages.length > 0) console.log(meta.messages)
}
}
if (process.env.GENERATE_OG_IMAGES) {
// Create og image
const intro = await mdIntro(lang, site, slug)
await generateOgImage({ lang, site, slug, title: meta.data.title, intro })
}
}
}
fs.writeFileSync(
path.resolve('..', site, 'prebuild', `mdx.${lang}.js`),
`export default ${JSON.stringify(pages[lang])}`
)
}
// Create wrapper
fs.writeFileSync(
path.resolve('..', site, 'prebuild', `mdx.js`),
locales.map((l) => `import ${l} from './mdx.${l}.js'`).join('\n') +
'\n\n' +
`export default { ${locales.join()} }`
)
// Write list of all MDX paths (in one language)
fs.writeFileSync(
path.resolve('..', site, 'prebuild', `mdx.paths.mjs`),
`export const mdxPaths = ${JSON.stringify(Object.keys(pages.en))}`
)
return pages
}