script/component-status-project/build.ts (67 lines of code) (raw):
import fs from 'fs'
import path from 'path'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const fm = require('front-matter') // FIXME after this bugfix is merged https://github.com/jxson/front-matter/pull/77
const sourceDirectory = path.resolve(__dirname, '../../docs/content/')
const outputDir = path.resolve(__dirname, '../../dist/')
type ComponentStatus = {
[component: string]: string
}
/**
* Extracts the component status for each file in the given directory.
*
* @param filenames Array of filenames to read front-matter from
* @param dir Absolute path to directory containing files
* @returns A promise that resolves to an array containing outcome of file front-matter extraction
*/
function getComponentStatuses(filenames: string[], dir: string) {
const promises: Promise<ComponentStatus | null>[] = []
const handleCallback = (
filename: string,
resolve: (value: ComponentStatus | null) => void,
reject: (value: unknown) => void
) => {
fs.readFile(path.resolve(dir, filename), 'utf-8', (err, content) => {
if (err) return reject(err)
if (fm.test(content)) {
const {
attributes: {title, status}
} = fm(content)
if (status) {
return resolve({[title]: status})
}
}
resolve(null)
})
}
for (const filename of filenames) {
const promise: Promise<ComponentStatus | null> = new Promise((resolve, reject) => {
return handleCallback(filename, resolve, reject)
})
promises.push(promise)
}
return Promise.all(promises)
}
/**
* Orchestrates the process of reading component status for each file in the given directory.
*
* @param dir Directory to source files where status will be extracted from
* @returns A promise that resolves to an object containing component statuses
*/
async function readFiles(dir: string) {
try {
const dirContents = fs.readdirSync(dir, {withFileTypes: true})
const filenames = dirContents.filter(dirent => dirent.isFile()).map(dirent => dirent.name)
const componentStatuses = await getComponentStatuses(filenames, dir)
return componentStatuses
.filter(Boolean)
.reverse()
.reduce(
(acc, file) => ({
...acc,
...file
}),
{}
)
} catch (err) {
throw new Error(`error reading files: ${err}`)
}
}
/**
* Writes the component status to the given file.
*/
async function build() {
try {
const componentStatuses = await readFiles(sourceDirectory)
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir)
}
fs.writeFileSync(`${outputDir}/component-status.json`, JSON.stringify(componentStatuses))
} catch (error) {
throw new Error(`error building component status object: ${error}`)
}
}
build()