scripts/maintain-readme/index.mjs (85 lines of code) (raw):

import { readFile, writeFile } from 'node:fs/promises'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { pathFromRoot, projectRoot } from '../project-paths.mjs'; import { getPackageList } from './get-package-list.mjs'; import { getMakeTargets } from '../lib/get-make-targets.mjs'; import { format } from 'prettier'; import prettierConfig from '@guardian/prettier'; import { getTasksByPackage } from '../lib/get-tasks-by-package.mjs'; import _updateSection from 'update-section'; /** * * @param {object} options * @param {string} options.contents * @param {string} options.tag * @param {string} options.updates * @param {string} options.updater * @returns string */ const updateSection = ({ contents, tag, updates, updater }) => { const START = `<!-- START ${tag} -->`; const END = `<!-- END ${tag} -->`; /** @param {string} line */ function matchesStart(line) { return line.includes(START); } /** @param {string} line */ function matchesEnd(line) { return line.includes(END); } return _updateSection( contents, [ START, `<!-- THIS CONTENT IS AUTOGENERATED BY ${updater} -->`, '', updates, '', END, ].join('\n'), matchesStart, matchesEnd, ); }; const thisFilePath = fileURLToPath(import.meta.url); const thisFilePathFromRoot = pathFromRoot(thisFilePath); const readMePath = path.resolve(projectRoot, 'README.md'); let contents = await readFile(readMePath, 'utf8'); contents = updateSection({ contents, tag: 'PUBLISHED_PACKAGES', updates: await getPackageList(), updater: thisFilePathFromRoot, }); const makeTargets = await getMakeTargets(); const makeTargetsList = makeTargets .map(({ target, description }) => `- \`make ${target}\` _${description}_`) .join('\n'); const tasks = await getTasksByPackage(); const tasksList = [ '### Project-specific tasks', 'Project-specific tasks are defined as `scripts` in their `package.json`, and can be run with `make <project>:<script>`:', '', ]; let currentPkg = ''; for (const tasksByPkg of tasks) { const [thisPkg, scripts] = tasksByPkg; if (thisPkg !== currentPkg) { currentPkg = thisPkg; tasksList.push(`#### ${currentPkg}`); } for (const script of scripts) { tasksList.push(`- \`make ${thisPkg}:${script}\``); } } contents = updateSection({ contents, tag: 'TASKS', updates: makeTargetsList + '\n\n' + tasksList.join('\n'), updater: thisFilePathFromRoot, }); await writeFile( readMePath, await format(contents, { filepath: readMePath, ...prettierConfig }), );