import * as fs from 'fs'; import * as _ from 'lodash'; import * as moment from 'moment'; import * as path from 'path'; import { exec as execAsync } from 'promisify-child-process'; import semver = require('semver'); import { constants } from '../constants'; import { Change, Changelog, VersionChangelog } from '../types'; const CHANGELOG_MD_HEADER = ` CHANGELOG `; export const changelogUtils = { getChangelogMdTitle(versionChangelog: VersionChangelog): string { // Use UTC rather than the local machines time (formatted date time is +0:00) const date = moment.utc(`${versionChangelog.timestamp}`, 'X').format('MMMM D, YYYY'); const title = `\n## v${versionChangelog.version} - _${date}_\n\n`; return title; }, getChangelogMdChange(change: Change): string { let line = ` * ${change.note}`; if (!_.isUndefined(change.pr)) { line += ` (#${change.pr})`; } return line; }, generateChangelogMd(changelog: Changelog): string { let changelogMd = CHANGELOG_MD_HEADER; _.each(changelog, versionChangelog => { const title = changelogUtils.getChangelogMdTitle(versionChangelog); changelogMd += title; const changelogVersionLines = _.map( versionChangelog.changes, changelogUtils.getChangelogMdChange.bind(changelogUtils), ); changelogMd += `${_.join(changelogVersionLines, '\n')}`; }); return changelogMd; }, shouldAddNewChangelogEntry(packageName: string, currentVersion: string, changelog: Changelog): boolean { if (_.isEmpty(changelog)) { return true; } const lastEntry = changelog[0]; if (semver.lt(lastEntry.version, currentVersion)) { throw new Error( `Found CHANGELOG version lower then current package version. ${packageName} current: ${currentVersion}, Changelog: ${ lastEntry.version }`, ); } const isLastEntryCurrentVersion = lastEntry.version === currentVersion; return isLastEntryCurrentVersion; }, getChangelogJSONIfExists(changelogPath: string): string | undefined { try { const changelogJSON = fs.readFileSync(changelogPath, 'utf-8'); return changelogJSON; } catch (err) { return undefined; } }, getChangelogOrCreateIfMissing(packageName: string, packageLocation: string): Changelog { const changelogJSONPath = path.join(packageLocation, 'CHANGELOG.json'); let changelogJsonIfExists = changelogUtils.getChangelogJSONIfExists(changelogJSONPath); if (_.isUndefined(changelogJsonIfExists)) { // If none exists, create new, empty one. changelogJsonIfExists = '[]'; fs.writeFileSync(changelogJSONPath, changelogJsonIfExists); } let changelog: Changelog; try { changelog = JSON.parse(changelogJsonIfExists); } catch (err) { throw new Error(`${packageName}'s CHANGELOG.json contains invalid JSON. Please fix and try again.`); } return changelog; }, async writeChangelogJsonFileAsync(packageLocation: string, changelog: Changelog): Promise { const changelogJSONPath = path.join(packageLocation, 'CHANGELOG.json'); fs.writeFileSync(changelogJSONPath, JSON.stringify(changelog, null, '\t')); await changelogUtils.prettifyAsync(changelogJSONPath, constants.monorepoRootPath); }, async writeChangelogMdFileAsync(packageLocation: string, changelogMdString: string): Promise { const changelogMarkdownPath = path.join(packageLocation, 'CHANGELOG.md'); fs.writeFileSync(changelogMarkdownPath, changelogMdString); await changelogUtils.prettifyAsync(changelogMarkdownPath, constants.monorepoRootPath); }, async prettifyAsync(filePath: string, cwd: string): Promise { await execAsync(`prettier --write ${filePath} --config .prettierrc`, { cwd, }); }, };