diff --git a/tools/.gitignore b/tools/.gitignore index 3c3629e..cd98105 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -1 +1,2 @@ node_modules +.idea/ \ No newline at end of file diff --git a/tools/package-lock.json b/tools/package-lock.json index 55db4ca..796bfa2 100644 --- a/tools/package-lock.json +++ b/tools/package-lock.json @@ -10,7 +10,8 @@ "license": "LGPL-3.0", "dependencies": { "@egjs/list-differ": "^1.0.1", - "@ltd/j-toml": "^1.38.0", + "@iarna/toml": "^2.2.5", + "@types/iarna__toml": "^2.0.5", "@types/json-stable-stringify-without-jsonify": "^1.0.1", "dedent-js": "^1.0.1", "json-stable-stringify-without-jsonify": "^1.0.1" @@ -229,6 +230,11 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@iarna/toml": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", @@ -269,11 +275,6 @@ "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", "dev": true }, - "node_modules/@ltd/j-toml": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/@ltd/j-toml/-/j-toml-1.38.0.tgz", - "integrity": "sha512-lYtBcmvHustHQtg4X7TXUu1Xa/tbLC3p2wLvgQI+fWVySguVZJF60Snxijw5EiohumxZbR10kWYFFebh1zotiw==" - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -563,6 +564,14 @@ "@types/node": "*" } }, + "node_modules/@types/iarna__toml": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/iarna__toml/-/iarna__toml-2.0.5.tgz", + "integrity": "sha512-I55y+SxI0ayM4MBU6yfGJGmi4wRll6wtSeKiFYAZj+Z5Q1DVbMgBSVDYY+xQZbjIlLs/pN4fidnvR8faDrmxPg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/imagemin": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@types/imagemin/-/imagemin-8.0.0.tgz", @@ -650,8 +659,7 @@ "node_modules/@types/node": { "version": "18.11.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", - "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", - "dev": true + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" }, "node_modules/@types/q": { "version": "1.5.5", @@ -11946,6 +11954,11 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@iarna/toml": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" + }, "@jridgewell/resolve-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", @@ -11983,11 +11996,6 @@ "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", "dev": true }, - "@ltd/j-toml": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/@ltd/j-toml/-/j-toml-1.38.0.tgz", - "integrity": "sha512-lYtBcmvHustHQtg4X7TXUu1Xa/tbLC3p2wLvgQI+fWVySguVZJF60Snxijw5EiohumxZbR10kWYFFebh1zotiw==" - }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -12257,6 +12265,14 @@ "@types/node": "*" } }, + "@types/iarna__toml": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/iarna__toml/-/iarna__toml-2.0.5.tgz", + "integrity": "sha512-I55y+SxI0ayM4MBU6yfGJGmi4wRll6wtSeKiFYAZj+Z5Q1DVbMgBSVDYY+xQZbjIlLs/pN4fidnvR8faDrmxPg==", + "requires": { + "@types/node": "*" + } + }, "@types/imagemin": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@types/imagemin/-/imagemin-8.0.0.tgz", @@ -12344,8 +12360,7 @@ "@types/node": { "version": "18.11.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", - "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", - "dev": true + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" }, "@types/q": { "version": "1.5.5", diff --git a/tools/package.json b/tools/package.json index 68e39c3..835b84b 100644 --- a/tools/package.json +++ b/tools/package.json @@ -51,7 +51,8 @@ }, "dependencies": { "@egjs/list-differ": "^1.0.1", - "@ltd/j-toml": "^1.38.0", + "@iarna/toml": "^2.2.5", + "@types/iarna__toml": "^2.0.5", "@types/json-stable-stringify-without-jsonify": "^1.0.1", "dedent-js": "^1.0.1", "json-stable-stringify-without-jsonify": "^1.0.1" diff --git a/tools/tasks/changelog/changelogData.ts b/tools/tasks/changelog/changelogData.ts index 76340ec..d755f6b 100644 --- a/tools/tasks/changelog/changelogData.ts +++ b/tools/tasks/changelog/changelogData.ts @@ -1,4 +1,4 @@ -import { Commit, FixUpInfo, InputReleaseType } from "../../types/changelogTypes"; +import { Commit, FixUpInfo, InputReleaseType, ParsedModInfo } from "../../types/changelogTypes"; import { getLastGitTag, getTags, isEnvVariableSet } from "../../util/util"; export default class ChangelogData { @@ -19,6 +19,9 @@ export default class ChangelogData { // Set of tags tags: Set; + // Map of project IDs to info text and/or details + modInfoList: Map; + /** * A normal initialisation. */ @@ -56,6 +59,8 @@ export default class ChangelogData { this.combineList = new Map(); this.tags = new Set(await getTags(this.to)); + + this.modInfoList = new Map(); } shouldIterate(): boolean { @@ -89,6 +94,8 @@ export default class ChangelogData { this.shaList = new Set(); this.combineList = new Map(); + this.modInfoList = new Map(); + // Tags list is fine because the `to` stays the same } } diff --git a/tools/tasks/changelog/createChangelog.ts b/tools/tasks/changelog/createChangelog.ts index a3868c1..00ab85a 100644 --- a/tools/tasks/changelog/createChangelog.ts +++ b/tools/tasks/changelog/createChangelog.ts @@ -11,7 +11,6 @@ import { specialParserSetup } from "./specialParser"; import generateModChanges from "./generateModChanges"; import pushAll, { pushChangelog, pushSeperator, pushTitle } from "./pusher"; import log from "fancy-log"; -import * as util from "util"; /** * Generates a changelog based on environmental variables, and saves it a changelog data class. diff --git a/tools/tasks/changelog/definitions.ts b/tools/tasks/changelog/definitions.ts index 1be4de4..a05e954 100644 --- a/tools/tasks/changelog/definitions.ts +++ b/tools/tasks/changelog/definitions.ts @@ -21,12 +21,16 @@ export const expandKey = "[EXPAND]"; export const expandList = "messages"; export const detailsKey = "[DETAILS]"; export const detailsList = "details"; +export const detailsRoot = "detail"; export const noCategoryKey = "[NO CATEGORY]"; export const combineKey = "[COMBINE]"; export const combineList = "commits"; +export const combineRoot = "commit"; export const fixUpKey = "[FIXUP]"; export const fixUpList = "fixes"; export const ignoreKey = "[IGNORE]"; +export const modInfoKey = "[MOD INFO]"; +export const modInfoList = "infos"; /* Sub Category Keys */ // Mode Category Keys @@ -201,21 +205,22 @@ export interface ModChangesAllocation { template: string; } +// These templates must be triple bracketed, because we don't want these to be html safe export const modChangesAllocations: Record = { added: { category: generalCategory, subCategory: modAdditions, - template: "{{ modName }}: *v{{ newVersion }}*", + template: "{{{ modName }}}: *v{{{ newVersion }}}*", }, updated: { category: generalCategory, subCategory: modUpdates, - template: "{{ modName }}: *v{{ oldVersion }} ⇥ v{{ newVersion }}*", + template: "{{{ modName }}}: *v{{{ oldVersion }}} ⇥ v{{{ newVersion }}}*", }, removed: { category: generalCategory, subCategory: modRemovals, - template: "{{ modName }}: *v{{ oldVersion }}*", + template: "{{{ modName }}}: *v{{{ oldVersion }}}*", }, }; diff --git a/tools/tasks/changelog/generateModChanges.ts b/tools/tasks/changelog/generateModChanges.ts index 70cb4eb..bcf3889 100644 --- a/tools/tasks/changelog/generateModChanges.ts +++ b/tools/tasks/changelog/generateModChanges.ts @@ -1,10 +1,10 @@ import { cleanupVersion, compareAndExpandManifestDependencies, getChangelog, getFileAtRevision } from "../../util/util"; import { ModpackManifest, ModpackManifestFile } from "../../types/modpackManifest"; -import { Commit, ModChangeInfo } from "../../types/changelogTypes"; +import { ChangelogMessage, Commit, ModChangeInfo } from "../../types/changelogTypes"; import ListDiffer, { DiffResult } from "@egjs/list-differ"; import dedent from "dedent-js"; import mustache from "mustache"; -import { defaultIndentation, modChangesAllocations, repoLink } from "./definitions"; +import { modChangesAllocations, repoLink } from "./definitions"; import ChangelogData from "./changelogData"; import { SpecialChangelogFormatting } from "../../types/changelogTypes"; import { sortCommitListReverse } from "./pusher"; @@ -15,9 +15,11 @@ import { error } from "fancy-log"; */ const getModChangesFormatting: (commits: Commit[]) => SpecialChangelogFormatting = (commits) => { return { - formatting: (changelogMessage, commits) => { - const indentation = changelogMessage.indentation == undefined ? defaultIndentation : changelogMessage.indentation; - const message = changelogMessage.commitMessage.trim(); + formatting: (message, subMessage, indentation, commits) => { + // Sub messages are details, so make them bold & italic + if (subMessage) { + return `${indentation}* ***${message}***`; + } if (commits.length > 1) { const authors: string[] = []; const formattedCommits: string[] = []; @@ -89,32 +91,60 @@ export default async function generateModChanges(data: ChangelogData): Promise { + detail.specialFormatting = getModChangesFormatting(commits); + return detail; + }); + } + + return { + commitMessage: text, + specialFormatting: getModChangesFormatting(commits), + subChangelogMessages: subMessages, + }; } /** diff --git a/tools/tasks/changelog/parser.ts b/tools/tasks/changelog/parser.ts index ee9b5b4..c39c4bc 100644 --- a/tools/tasks/changelog/parser.ts +++ b/tools/tasks/changelog/parser.ts @@ -6,9 +6,10 @@ import { detailsKey, expandKey, ignoreKey, + modInfoKey, noCategoryKey, } from "./definitions"; -import { parseCombine, parseDetails, parseExpand, parseIgnore } from "./specialParser"; +import { parseCombine, parseDetails, parseExpand, parseIgnore, parseModInfo } from "./specialParser"; import { getChangelog } from "../../util/util"; import ChangelogData from "./changelogData"; @@ -68,6 +69,7 @@ export async function parseCommitBody( // Only return if ignore is not undefined if (ignore) return ignore; } + if (commitBody.includes(modInfoKey)) await parseModInfo(commitBody, commitObject); if (commitBody.includes(detailsKey)) { await parseDetails(commitMessage, commitBody, commitObject, parser); return true; diff --git a/tools/tasks/changelog/pusher.ts b/tools/tasks/changelog/pusher.ts index 6daa894..022b8de 100644 --- a/tools/tasks/changelog/pusher.ts +++ b/tools/tasks/changelog/pusher.ts @@ -174,15 +174,20 @@ export function sortCommitListReverse(list: Commit[]): void { * @return string Formatted Changelog Message */ async function formatChangelogMessage(changelogMessage: ChangelogMessage, subMessage = false): Promise { - if (changelogMessage.specialFormatting) - return changelogMessage.specialFormatting.formatting(changelogMessage, changelogMessage.specialFormatting.storage); - const indentation = changelogMessage.indentation == undefined ? defaultIndentation : changelogMessage.indentation; let message = changelogMessage.commitMessage.trim(); // Transform PR and/or Issue tags into a link. message = await transformTags(message); + if (changelogMessage.specialFormatting) + return changelogMessage.specialFormatting.formatting( + message, + subMessage, + indentation, + changelogMessage.specialFormatting.storage, + ); + if (changelogMessage.commitObject && !subMessage) { if (data.combineList.has(changelogMessage.commitObject.hash)) { const commits = data.combineList.get(changelogMessage.commitObject.hash); diff --git a/tools/tasks/changelog/specialParser.ts b/tools/tasks/changelog/specialParser.ts index 526f27b..25f9e59 100644 --- a/tools/tasks/changelog/specialParser.ts +++ b/tools/tasks/changelog/specialParser.ts @@ -6,17 +6,21 @@ import { Ignored, IgnoreInfo, IgnoreLogic, + ModInfo, + ParsedModInfo, Parser, } from "../../types/changelogTypes"; import dedent from "dedent-js"; import matter, { GrayMatterFile } from "gray-matter"; -import toml from "@ltd/j-toml"; +import toml from "@iarna/toml"; import { combineKey, combineList, + combineRoot, defaultIgnoreLogic, detailsKey, detailsList, + detailsRoot, expandKey, expandList, fixUpKey, @@ -25,6 +29,8 @@ import { ignoreKey, ignoreLogics, indentationLevel, + modInfoKey, + modInfoList, } from "./definitions"; import { findCategories, findSubCategory } from "./parser"; import ChangelogData from "./changelogData"; @@ -124,16 +130,17 @@ export async function parseIgnore(commitBody: string, commitObject: Commit): Pro */ export async function parseFixUp(commit: Commit): Promise { if (!commit.body || !commit.body.includes(fixUpKey)) return false; - await parseTOMLToList( + await parseTOMLWithRootToList( commit.body, commit, fixUpKey, fixUpList, - (item: FixUpInfo) => item.sha && item.newTitle, - (item) => { + (item) => !item.sha || !item.newTitle, + async (item) => { // Only override if no other overrides, from newer commits, set if (!data.commitFixes.has(item.sha)) data.commitFixes.set(item.sha, item); }, + (item) => item as unknown as FixUpInfo, (matter) => { // Must override, even if newer commits specified changes, as need to remove fixup data data.commitFixes.set(commit.hash, { @@ -147,16 +154,58 @@ export async function parseFixUp(commit: Commit): Promise { return true; } +/** + * Parses a commit with 'mod info'. + */ +export async function parseModInfo(commitBody: string, commitObject: Commit): Promise { + await parseTOMLWithRootToList( + commitBody, + commitObject, + modInfoKey, + modInfoList, + (item): boolean => { + const invalidProjectID = !item.projectID || typeof item.projectID !== "number" || Number.isNaN(item.projectID); + const invalidInfo = !item.info; + const invalidRootDetails = !item.detail; + const invalidDetails = !item.details || !Array.isArray(item.details) || !(item.details.length > 0); + // Invalid if invalid ID, or invalid info and invalid details + return invalidProjectID || (invalidInfo && invalidRootDetails && invalidDetails); + }, + async (item) => { + data.modInfoList.set(item.projectID, await getParsedModInfo(item)); + }, + ); +} + +/** + * Gets the parsed mod info of a mod info. + */ +async function getParsedModInfo(modInfo: ModInfo): Promise { + const subMessages: ChangelogMessage[] = []; + if (modInfo.detail) subMessages.push({ commitMessage: modInfo.detail, indentation: indentationLevel }); + if (modInfo.details && modInfo.details.length > 0) + subMessages.push( + ...modInfo.details.map((detail) => { + return { commitMessage: detail, indentation: indentationLevel }; + }), + ); + + return { + info: modInfo.info, + details: subMessages, + }; +} + /** * Parses a commit with 'expand'. */ export async function parseExpand(commitBody: string, commitObject: Commit, parser: Parser): Promise { - await parseTOMLToList( + await parseTOMLWithRootToList( commitBody, commitObject, expandKey, expandList, - (item: ExpandedMessage) => item.messageTitle, + (item) => !item.messageTitle, async (item) => { const title = dedent(item.messageTitle); @@ -214,12 +263,12 @@ async function expandDetailsLevel( indentation = indentationLevel, ): Promise { const result: ChangelogMessage[] = []; - await parseTOMLToList( + await parseTOMLWithRootToList( commitBody, commitObject, detailsKey, detailsList, - (item: string) => item, + (item) => !item, async (item) => { item = dedent(item).trim(); if (item.includes(detailsKey)) { @@ -228,6 +277,7 @@ async function expandDetailsLevel( result.push({ commitMessage: item, commitObject: commitObject, indentation: indentation }); } }, + (root) => root[detailsRoot] as string, ); return result; } @@ -236,16 +286,17 @@ async function expandDetailsLevel( * Parses a commit with 'combine'. */ export async function parseCombine(commitBody: string, commitObject: Commit): Promise { - await parseTOMLToList( + await parseTOMLWithRootToList( commitBody, commitObject, combineKey, combineList, - (item: string) => item, - (item: string) => { + (item) => !item, + async (item) => { if (!data.combineList.has(item)) data.combineList.set(item, []); data.combineList.get(item).push(commitObject); }, + (root) => root[combineRoot] as string, ); } @@ -280,7 +331,7 @@ async function parseTOML( delimiters: delimiter, engines: { toml: (input): Record => { - return toml.parse(input, "\n"); + return toml.parse(input); }, }, language: "toml", @@ -314,50 +365,34 @@ async function parseTOML( } /** - * Parse TOML in a commit body to produce a list. - * @param commitBody The body to parse - * @param commitObject The commit object to grab messages from, and to determine error messages. - * @param delimiter The delimiters, surrounding the TOML. - * @param listKey The key of the list to parse. - * @param emptyCheck The check to see if an item in the list is invalid. - * @param perItemCallback The callback to perform on each item in the list. - * @param matterCallback An optional callback to perform on the matter. + * Parses a List with safety. + * @param messages List to parse + * @param listKey List Key, used for error messages + * @param commitBody The commit body + * @param commitObject The commit object, used for error messages. + * @param endMessage The end message to use + * @param emptyCheck Check to see if an item is valid. True -> Empty + * @param perItemCallback Callback to perform per item. Async to allow for further toml parsing + * @param entryModifier Callback to change the entry in error messages. Optional. Defaults to `i + 1`. */ -async function parseTOMLToList( +async function parseList( + messages: T[], + listKey: string, commitBody: string, commitObject: Commit, - delimiter: string, - listKey: string, - emptyCheck: (item: T) => string, - perItemCallback: (item: T) => void, - matterCallback?: (matter: GrayMatterFile) => void, -): Promise { - const messages = await parseTOML(commitBody, commitObject, delimiter, listKey, matterCallback); - const endMessage = getEndMessage(delimiter); - - if (!messages || !Array.isArray(messages) || messages.length === 0) { - error(dedent` - List (key: '${listKey}') in body: - \`\`\` - ${commitBody}\`\`\` - of commit object ${commitObject.hash} (${commitObject.message}) is empty, not a list, or does not exist.`); - - if (commitObject.body && commitBody !== commitObject.body) { - error(dedent` - Original Body: - \`\`\` - ${commitObject.body}\`\`\``); - } - error(`${endMessage}\n`); - - if (data.isTest) throw new Error("Failed Parsing Message List. See Above."); - return; - } + endMessage: string, + emptyCheck: (item: T) => boolean, + perItemCallback: (item: T) => Promise, + entryModifier?: (index: number) => number, +) { for (let i = 0; i < messages.length; i++) { const item = messages[i]; - if (!emptyCheck(item)) { + if (emptyCheck(item)) { + let index = i + 1; + if (entryModifier) index = entryModifier(i); + error(dedent` - Missing Requirements for entry ${i + 1} in body: + Missing Requirements for entry ${index} of list with key '${listKey}' in body: \`\`\` ${commitBody}\`\`\` of commit object ${commitObject.hash} (${commitObject.message}).`); @@ -373,10 +408,157 @@ async function parseTOMLToList( if (data.isTest) throw new Error("Bad Entry. See Above."); continue; } - perItemCallback(item); + await perItemCallback(item); } } +/** + * Parses TOML in a commit body to produce a list. List includes items in the base of the toml (added first). + * List just contains root item if none in list. + * @param commitBody The body to parse + * @param commitObject The commit object to grab messages from, and to determine error messages. + * @param delimiter The delimiters, surrounding the TOML. + * @param listKey The key of the list to parse. + * @param rootObjTransform Transform root obj into T. Return undefined if invalid. + * @param emptyCheck The check to see if an item in the list is invalid. True -> Invalid/Empty + * @param perItemCallback The callback to perform on each item in the list. Async to allow for further toml parsing + * @param matterCallback An optional callback to perform on the matter. + */ +async function parseTOMLWithRootToList( + commitBody: string, + commitObject: Commit, + delimiter: string, + listKey: string, + emptyCheck: (item: T) => boolean, + perItemCallback: (item: T) => Promise, + rootObjTransform?: (root: Record) => T, + matterCallback?: (matter: GrayMatterFile) => void, +): Promise { + let root: Record; + const messages: T[] = []; + + const endMessage = getEndMessage(delimiter); + + // Parse Root TOML + try { + root = await parseTOML>(commitBody, commitObject, delimiter, null, matterCallback); + const rootObj = rootObjTransform ? rootObjTransform(root) : (root as T); + // Only push root if it passes empty check + if (rootObj && !emptyCheck(rootObj)) messages.push(rootObj); + } catch (e) { + error(dedent` + Failed parsing Root TOML in body: + \`\`\` + ${commitBody}\`\`\` + of commit object ${commitObject.hash} (${commitObject.message}). + This could be because of invalid syntax.`); + + if (commitObject.body && commitBody !== commitObject.body) { + error(dedent` + Original Body: + \`\`\` + ${commitObject.body}\`\`\``); + } + + error(`\n${endMessage}\n`); + if (data.isTest) throw e; + return undefined; + } + + // No List + if (!(listKey in root)) { + if (messages.length > 0) { + await perItemCallback(messages[0]); + return; + } + // No Valid Entry + error(dedent` + Missing Requirements for root entry, & no list with list key ${listKey} detected in body: + \`\`\` + ${commitBody}\`\`\` + of commit object ${commitObject.hash} (${commitObject.message}).`); + + if (commitObject.body && commitBody !== commitObject.body) { + error(dedent` + Original Body: + \`\`\` + ${commitObject.body}\`\`\``); + } + error(`${endMessage}\n`); + + if (data.isTest) throw new Error("No Valid Entry. See Above."); + } + + // Parse List TOML + if (!root[listKey] || !Array.isArray(root[listKey])) { + error(dedent` + List (key: '${listKey}') in body: + \`\`\` + ${commitBody}\`\`\` + of commit object ${commitObject.hash} (${commitObject.message}) not a list, or does not exist.`); + + if (commitObject.body && commitBody !== commitObject.body) { + error(dedent` + Original Body: + \`\`\` + ${commitObject.body}\`\`\``); + } + error(`${endMessage}\n`); + + if (data.isTest) throw new Error("Failed Parsing List. See Above."); + return; + } + + let list: T[]; + try { + list = root[listKey] as unknown as T[]; + } catch (e) { + error(dedent` + List (key: '${listKey}') in body: + \`\`\` + ${commitBody}\`\`\` + of commit object ${commitObject.hash} (${commitObject.message}) could not be turned into correct list type.`); + + if (commitObject.body && commitBody !== commitObject.body) { + error(dedent` + Original Body: + \`\`\` + ${commitObject.body}\`\`\``); + } + error(`${endMessage}\n`); + + if (data.isTest) throw new Error("Failed Parsing List. See Above."); + return; + } + if (list.length === 0) { + error(dedent` + List (key: '${listKey}') in body: + \`\`\` + ${commitBody}\`\`\` + of commit object ${commitObject.hash} (${commitObject.message}) is empty.`); + + if (commitObject.body && commitBody !== commitObject.body) { + error(dedent` + Original Body: + \`\`\` + ${commitObject.body}\`\`\``); + } + error(`${endMessage}\n`); + + if (data.isTest) throw new Error("Failed Parsing List. See Above."); + return; + } + + if (messages.length > 0) { + messages.push(...list); + // Because we've already done empty check on root obj, no need to suppress error msg + // Keep as index (root: 0, obj1: 1, obj2: 2, ...) + await parseList(messages, listKey, commitBody, commitObject, endMessage, emptyCheck, perItemCallback, (i) => i); + } + // Normal Parsing of List + else await parseList(list, listKey, commitBody, commitObject, endMessage, emptyCheck, perItemCallback); +} + function getEndMessage(delimiter: string) { if (data.isTest) { return dedent` diff --git a/tools/types/changelogTypes.ts b/tools/types/changelogTypes.ts index feac347..0337e7a 100644 --- a/tools/types/changelogTypes.ts +++ b/tools/types/changelogTypes.ts @@ -122,8 +122,12 @@ export interface ChangelogMessage { export interface SpecialChangelogFormatting { /** * Formatting Function + * @param message The transformed message (trimmed, transformed issue/pr tags) + * @param subMessage Whether this message is a sub message (true if yes) + * @param indentation The indentation level to use + * @param storage May be null, is the defined storage in this interface */ - formatting: (message: ChangelogMessage, storage?: T) => string; + formatting: (message: string, subMessage: boolean, indentation: string, storage?: T) => string; /** * Storage @@ -256,6 +260,18 @@ export interface FixUpInfo { newBody?: string; } +export interface ModInfo { + projectID: number; + info?: string; + detail?: string; + details?: string[]; +} + +export interface ParsedModInfo { + info?: string; + details?: ChangelogMessage[]; +} + export type InputReleaseType = "Release" | "Beta Release" | "Alpha Release" | "Cutting Edge Build"; export interface DeployReleaseType {