UNPKG

libphonenumber-metadata-generator

Version:

Metadata generator for `libphonenumber-js`

210 lines (185 loc) 6.55 kB
// Parses `release_notes.txt` file from Google's `libphonenumber` GitHub repository. export default function parseReleaseNotes(text, versionsInfo = []) { // Get a block of release notes that describes a released version. const [versionNotes, restText] = getVersionNotes(text) // Read the first line of the release notes. // It contains the release date and the version number. // Example: "Aug 13, 2025: v9.0.12" const [dateAndVersionText, textAfterDateAndVersion] = readLine(versionNotes) // Old release notes entries might have a "\n\n" sequence after the "date and version" line. // Skip such old release notes entries. if (textAfterDateAndVersion) { // Parse `date` and `version` from the first line of text. // `date` example: "Nov 23, 2016". // `version` example: "9.0.12". const dateAndVersion = parseDateAndVersion(dateAndVersionText) if (dateAndVersion) { const { date, version } = dateAndVersion // Read the metadata changes. const metadataChanges = parseMetadataChanges(textAfterDateAndVersion) // Add this version's info to the list and proceed recursively. versionsInfo = versionsInfo.concat([{ date, version, changes: metadataChanges }]) } } // If there're more release notes. if (restText) { return parseReleaseNotes(restText, versionsInfo) } // If there're no more release notes, return the result. return versionsInfo } const METADATA_CHANGES_START_MARKER = 'Metadata changes:' const METADATA_CHANGES_ITEM_START_MARKER = ' - ' const METADATA_CHANGES_ITEM_NEW_LINE_MARKER = ' ' // Parses metadata changes from text. // Finds "Metadata changes:" line, then reads everything below it that starts with a " - ". // Returns a list of metadata changes (an array of strings). function parseMetadataChanges(text) { const metadataChanges = [] // First, it looks for a "Metadata changes:" line which denotes a start of metadata changes block. const metadataChangesStartsAt = text.indexOf(METADATA_CHANGES_START_MARKER) if (metadataChangesStartsAt >= 0) { // If the "Metadata changes:" line is found, the next lines describe metadata changes. // Those lines start with either " - " or " ". // If it encounters anything different then it assumes the end of metadata changes block. let metadataChangesAndRest = text.slice(metadataChangesStartsAt + METADATA_CHANGES_START_MARKER.length + '\n'.length) while (metadataChangesAndRest) { const [line, rest] = readLine(metadataChangesAndRest) if (line.indexOf(METADATA_CHANGES_ITEM_START_MARKER) === 0) { // If it encounters a "new list item" sequence, it adds a new entry in the list of changes, metadataChanges.push(line.slice(METADATA_CHANGES_ITEM_START_MARKER.length)) } else if (line.indexOf(METADATA_CHANGES_ITEM_NEW_LINE_MARKER) === 0) { // If it encounters a "new line" sequence within a given "list item", // it appends the text on the new line to the text of the latest entry in the list of changes. metadataChanges[metadataChanges.length - 1] += '\n' + line.slice(METADATA_CHANGES_ITEM_NEW_LINE_MARKER.length) } else { // If it encounters anything different then it assumes the end of metadata changes block. break } metadataChangesAndRest = rest } } return metadataChanges } // Reads the top line from the text. // Returns that line of text and the rest of the text. function readLine(text) { const indexOfNewLineCharacter = text.indexOf('\n') if (indexOfNewLineCharacter >= 0) { return [ text.slice(0, indexOfNewLineCharacter), text.slice(indexOfNewLineCharacter + '\n'.length) ] } return [text, undefined] } // Splits `text` into a "version notes" block and "rest text". function getVersionNotes(text) { // Read the latest version's entry of the release notes. const versionBlockSeparatorStartsAt = text.indexOf(VERSION_BLOCK_SEPARATOR) if (versionBlockSeparatorStartsAt >= 0) { const versionNotes = text.slice(0, versionBlockSeparatorStartsAt) const restText = text.slice(versionBlockSeparatorStartsAt + VERSION_BLOCK_SEPARATOR.length) return [versionNotes, restText] } else { return [text, undefined] } } // Parses `date` and `version` from a line of text. // `date` example: "Nov 23, 2016". // `version` example: "9.0.12". function parseDateAndVersion(dateAndVersion) { // Example: "Aug 13, 2025: v9.0.12" let match = dateAndVersion.match(DATE_AND_VERSION_REGEXP_3) if (match) { return { date: new Date(Date.UTC( getYearNumber(match[3]), getMonthNumber(match[1]) - 1, getDayNumber(match[2]) )), version: match[4] } } // Example: "August 11th, 2011: libphonenumber-3.8" match = dateAndVersion.match(DATE_AND_VERSION_REGEXP_2) if (match) { return { date: new Date(Date.UTC( getYearNumber(match[3]), getMonthNumber(match[1]) - 1, getDayNumber(match[2]) )), version: match[4] } } // Example: "July 30th, 2010" match = dateAndVersion.match(DATE_AND_VERSION_REGEXP_1) if (match) { return { date: new Date(Date.UTC( getYearNumber(match[3]), getMonthNumber(match[1]) - 1, getDayNumber(match[2]) )), version: match[4] } } } function getYearNumber(text) { return Number(text) } function getMonthNumber(text) { if (MONTHS_SHORT.includes(text)) { return MONTHS_SHORT.indexOf(text) + 1 } if (MONTHS_LONG.includes(text)) { return MONTHS_LONG.indexOf(text) + 1 } } function getDayNumber(text) { // Example: "02" → 2. return parseInt(text, 10) } const MONTHS_SHORT = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ] const MONTHS_LONG = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ] // The latest format of release notes entry header. // Example: "Nov 23, 2016: v7.7.5" const DATE_AND_VERSION_REGEXP_3 = /([A-Za-z]+) (\d+), (\d{4}): v([\d\.]+)$/ // One of the older formats of release notes entry header. // Example: "August 11th, 2011: libphonenumber-3.8" const DATE_AND_VERSION_REGEXP_2 = /([A-Za-z]+) (\d+)(?:th)?, (\d{4}): libphonenumber-([\d\.]+)$/ // The initial format of release notes entry header. // Example: "July 30th, 2010" const DATE_AND_VERSION_REGEXP_1 = /([A-Za-z]+) (\d+)(?:th)?, (\d{4})$/ const VERSION_BLOCK_SEPARATOR = '\n\n'