feat(shared): Add git info to prebuild step
This commit is contained in:
parent
c047b905df
commit
5b12ea49ec
5 changed files with 156 additions and 182 deletions
|
@ -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',
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
|
|
94
sites/shared/prebuild/git.mjs
Normal file
94
sites/shared/prebuild/git.mjs
Normal 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
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue