зеркало из https://github.com/github/docs.git
158 строки
5.3 KiB
JavaScript
Executable File
158 строки
5.3 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
// [start-readme]
|
|
//
|
|
// An automated test checks for discrepancies between category directory names and
|
|
// slugified category titles as IDs.
|
|
//
|
|
// If the test fails, a human needs to run this script to update the directory
|
|
// names and add appropriate redirects.
|
|
//
|
|
// **This script is not currently supported on Windows.**
|
|
//
|
|
// [end-readme]
|
|
|
|
import fs from 'fs'
|
|
import path from 'path'
|
|
import frontmatter from '../lib/read-frontmatter.js'
|
|
import walk from 'walk-sync'
|
|
import slash from 'slash'
|
|
import GithubSlugger from 'github-slugger'
|
|
import { XmlEntities } from 'html-entities'
|
|
import loadSiteData from '../lib/site-data.js'
|
|
import renderContent from '../lib/render-content/index.js'
|
|
|
|
const slugger = new GithubSlugger()
|
|
const entities = new XmlEntities()
|
|
|
|
const contentDir = path.join(process.cwd(), 'content')
|
|
|
|
// TODO fix path separators in the redirect
|
|
if (process.platform.startsWith('win')) {
|
|
console.log('This script cannot be run on Windows at this time! Exiting...')
|
|
process.exit()
|
|
}
|
|
|
|
// Execute!
|
|
main()
|
|
|
|
async function main() {
|
|
const englishCategoryIndices = getEnglishCategoryIndices()
|
|
const siteData = await getEnglishSiteData()
|
|
|
|
for (const categoryIndex of englishCategoryIndices) {
|
|
const contents = fs.readFileSync(categoryIndex, 'utf8')
|
|
const { data, content } = frontmatter(contents)
|
|
|
|
// Get the parent directory name
|
|
const categoryDirPath = path.dirname(categoryIndex)
|
|
const categoryDirName = path.basename(categoryDirPath)
|
|
|
|
const title = await renderContent(data.title, { site: siteData }, { textOnly: true })
|
|
slugger.reset()
|
|
const expectedSlug = slugger.slug(entities.decode(title))
|
|
|
|
// If the directory name already matches the expected slug, bail out now
|
|
if (categoryDirName === expectedSlug) continue
|
|
|
|
// Figure out the new path for the category
|
|
const categoryDirParentDir = path.dirname(categoryDirPath)
|
|
const newPath = path.join(categoryDirParentDir, expectedSlug)
|
|
|
|
// Figure out redirect path
|
|
const relativeOldPath = path.relative(contentDir, categoryDirPath)
|
|
const redirectPath = '/' + slash(relativeOldPath)
|
|
|
|
// Log it
|
|
const relativeNewPath = path.relative(contentDir, newPath)
|
|
console.log(`Renaming category directory:
|
|
Old: "${relativeOldPath}"
|
|
New: "${relativeNewPath}"
|
|
Redirect: "${redirectPath}"
|
|
`)
|
|
|
|
// Add a new redirect to the frontmatter
|
|
if (!data.redirect_from) {
|
|
data.redirect_from = []
|
|
}
|
|
data.redirect_from.push(redirectPath)
|
|
|
|
// Update the category index file on disk
|
|
fs.writeFileSync(categoryIndex, frontmatter.stringify(content, data, { lineWidth: 10000 }))
|
|
|
|
// Update all of the category's articles on disk as well to add a new redirect to their frontmatter
|
|
for (const articleFileName of fs.readdirSync(categoryDirPath)) {
|
|
const articlePath = path.join(categoryDirPath, articleFileName)
|
|
|
|
// Figure out redirect path
|
|
const articlePathMinusExtension = path.join(
|
|
categoryDirPath,
|
|
path.basename(articleFileName, '.md')
|
|
)
|
|
const redirectArticlePath = '/' + slash(path.relative(contentDir, articlePathMinusExtension))
|
|
|
|
// Log it
|
|
const relativeOldArticlePath = path.relative(contentDir, articlePath)
|
|
const newArticlePath = path.join(categoryDirParentDir, expectedSlug, articleFileName)
|
|
const relativeNewArticlePath = path.relative(contentDir, newArticlePath)
|
|
console.log(`Adding redirect to article:
|
|
Old: "${relativeOldArticlePath}"
|
|
New: "${relativeNewArticlePath}"
|
|
Redirect: "${redirectArticlePath}"
|
|
`)
|
|
|
|
const articleContents = fs.readFileSync(articlePath, 'utf8')
|
|
const { data: articleData, content: articleContent } = frontmatter(articleContents)
|
|
|
|
// Add a new redirect to the frontmatter
|
|
if (!articleData.redirect_from) {
|
|
articleData.redirect_from = []
|
|
}
|
|
articleData.redirect_from.push(redirectArticlePath)
|
|
|
|
// Update the article file on disk
|
|
fs.writeFileSync(
|
|
articlePath,
|
|
frontmatter.stringify(articleContent, articleData, { lineWidth: 10000 })
|
|
)
|
|
}
|
|
|
|
// Update the reference to this category in the product index file on disk
|
|
//
|
|
// NOTE: This approach may update the same product index multiple times per
|
|
// script run but TBH I'm OK with that in a manually executed script
|
|
const productIndexPath = path.join(categoryDirParentDir, 'index.md')
|
|
const productIndexContents = fs.readFileSync(productIndexPath, 'utf8')
|
|
const { data: productIndexData, content: productIndex } = frontmatter(productIndexContents)
|
|
const revisedProductIndex = productIndex.replace(
|
|
new RegExp(`(\\s+)(?:/${categoryDirName})(\\s+)`, 'g'),
|
|
`$1/${expectedSlug}$2`
|
|
)
|
|
fs.writeFileSync(
|
|
productIndexPath,
|
|
frontmatter.stringify(revisedProductIndex, productIndexData, { lineWidth: 10000 })
|
|
)
|
|
|
|
console.log(`*** Updated product index "${productIndexPath}" for ☝️\n`)
|
|
|
|
// Finally, rename the directory
|
|
fs.renameSync(categoryDirPath, newPath)
|
|
}
|
|
}
|
|
|
|
function getEnglishCategoryIndices() {
|
|
const walkOptions = {
|
|
globs: ['*/*/**/index.md'],
|
|
ignore: ['{rest,graphql,developers}/**', 'enterprise/admin/index.md', '**/articles/**'],
|
|
directories: false,
|
|
includeBasePath: true,
|
|
}
|
|
|
|
return walk(contentDir, walkOptions)
|
|
}
|
|
|
|
async function getEnglishSiteData() {
|
|
const siteData = await loadSiteData()
|
|
return siteData.en.site
|
|
}
|