1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 18:00:18 +01:00
metamask-extension/development/lib/changelog/parseChangelog.js
Mark Stacey 312f2afc41
Refactor changelog parsing and generation (#10847)
The `auto-changelog.js` script has been refactoring into various
different modules. This was done in preparation for migrating this to
a separate repository, where it can be used in our libraries as well.

Functionally this should act _mostly_ the same way, but there have been
some changes. It was difficult to make this a pure refactor because of
the strategy used to validate the changelog and ensure each addition
remained valid. Instead of being updated in-place, the changelog is now
parsed upfront and stored as a "Changelog" instance, which is a new
class that was written to allow only valid changes. The new changelog
is then stringified and completely overwrites the old one.

The parsing had to be much more strict, as any unanticipated content
would otherwise be erased unintentionally. This script now also
normalizes the formatting of the changelog (though the individual
change descriptions are still unformatted).

The changelog stringification now accommodates non-linear releases as
well. For example, you can now release v1.0.1 *after* v2.0.0, and it
will be listed in chronological order while also correctly constructing
the `compare` URLs for each release.
2021-04-08 16:14:30 -02:30

85 lines
2.8 KiB
JavaScript

const Changelog = require('./changelog');
const { unreleased } = require('./constants');
function truncated(line) {
return line.length > 80 ? `${line.slice(0, 80)}...` : line;
}
/**
* Constructs a Changelog instance that represents the given changelog, which
* is parsed for release and change informatino.
* @param {Object} options
* @param {string} options.changelogContent - The changelog to parse
* @param {string} options.repoUrl - The GitHub repository URL for the current
* project.
* @returns {Changelog} A changelog instance that reflects the changelog text
* provided.
*/
function parseChangelog({ changelogContent, repoUrl }) {
const changelogLines = changelogContent.split('\n');
const changelog = new Changelog({ repoUrl });
const unreleasedHeaderIndex = changelogLines.indexOf(`## [${unreleased}]`);
if (unreleasedHeaderIndex === -1) {
throw new Error(`Failed to find ${unreleased} header`);
}
const unreleasedLinkReferenceDefinition = changelogLines.findIndex((line) => {
return line.startsWith(`[${unreleased}]: `);
});
if (unreleasedLinkReferenceDefinition === -1) {
throw new Error(`Failed to find ${unreleased} link reference definition`);
}
const contentfulChangelogLines = changelogLines
.slice(unreleasedHeaderIndex + 1, unreleasedLinkReferenceDefinition)
.filter((line) => line !== '');
let mostRecentRelease;
let mostRecentCategory;
for (const line of contentfulChangelogLines) {
if (line.startsWith('## [')) {
const results = line.match(
/^## \[(\d+\.\d+\.\d+)\](?: - (\d\d\d\d-\d\d-\d\d))?(?: \[(\w+)\])?/u,
);
if (results === null) {
throw new Error(`Malformed release header: '${truncated(line)}'`);
}
mostRecentRelease = results[1];
mostRecentCategory = undefined;
const date = results[2];
const status = results[3];
changelog.addRelease({
addToStart: false,
date,
status,
version: mostRecentRelease,
});
} else if (line.startsWith('### ')) {
const results = line.match(/^### (\w+)$\b/u);
if (results === null) {
throw new Error(`Malformed category header: '${truncated(line)}'`);
}
mostRecentCategory = results[1];
} else if (line.startsWith('- ')) {
if (mostRecentCategory === undefined) {
throw new Error(`Category missing for change: '${truncated(line)}'`);
}
const description = line.slice(2);
changelog.addChange({
addToStart: false,
category: mostRecentCategory,
description,
version: mostRecentRelease,
});
} else if (mostRecentRelease === null) {
continue;
} else {
throw new Error(`Unrecognized line: '${truncated(line)}'`);
}
}
return changelog;
}
module.exports = { parseChangelog };