Allow Adding Info to Mod Changes & Refactor (#527)
Allows adding info/details to mod changes section of the changelog. Because this uses numbers, the TOML parser has been changed. The old parser produced weird artifacts when processing numbers. Refactor: For most tags (except for ignore), allows for optional lists. [MOD INFO] [[infos]] projectID = 620262 info = "Replaced by Alfheim Lighting Engine" [[infos]] projectID = 230976 info = "Replaced by Universal Tweaks" [[infos]] projectID = 271492 info = "Replaced by Universal Tweaks" [[infos]] projectID = 271740 info = "Replaced by Universal Tweaks" [[infos]] projectID = 278385 info = "Replaced by Universal Tweaks" [[infos]] projectID = 278494 info = "Replaced by Vintage Fix" [[infos]] projectID = 280510 info = "Replaced by Universal Tweaks" [[infos]] projectID = 284904 info = "Replaced by Universal Tweaks" [[infos]] projectID = 288885 info = "Replaced by Universal Tweaks" [MOD INFO]
This commit is contained in:
parent
15ff662307
commit
65bd92d9fa
1
tools/.gitignore
vendored
1
tools/.gitignore
vendored
@ -1 +1,2 @@
|
||||
node_modules
|
||||
.idea/
|
45
tools/package-lock.json
generated
45
tools/package-lock.json
generated
@ -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",
|
||||
|
@ -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"
|
||||
|
@ -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<string>;
|
||||
|
||||
// Map of project IDs to info text and/or details
|
||||
modInfoList: Map<number, ParsedModInfo>;
|
||||
|
||||
/**
|
||||
* A normal initialisation.
|
||||
*/
|
||||
@ -56,6 +59,8 @@ export default class ChangelogData {
|
||||
this.combineList = new Map<string, Commit[]>();
|
||||
|
||||
this.tags = new Set<string>(await getTags(this.to));
|
||||
|
||||
this.modInfoList = new Map<number, ParsedModInfo>();
|
||||
}
|
||||
|
||||
shouldIterate(): boolean {
|
||||
@ -89,6 +94,8 @@ export default class ChangelogData {
|
||||
this.shaList = new Set<string>();
|
||||
this.combineList = new Map<string, Commit[]>();
|
||||
|
||||
this.modInfoList = new Map<number, ParsedModInfo>();
|
||||
|
||||
// Tags list is fine because the `to` stays the same
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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<ModChangesType, ModChangesAllocation> = {
|
||||
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 }}}*",
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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<Commit[]> = (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<v
|
||||
// Sort array so newest commits appear at end instead of start of commit string
|
||||
sortCommitListReverse(commits);
|
||||
}
|
||||
block.allocation.category.changelogSection.get(block.allocation.subCategory).push({
|
||||
commitMessage: getModChangeMessage(info, block.allocation.template),
|
||||
specialFormatting: getModChangesFormatting(commits),
|
||||
});
|
||||
block.allocation.category.changelogSection
|
||||
.get(block.allocation.subCategory)
|
||||
.push(getModChangeMessage(info, block.allocation.template, data, commits));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message, determined by the parameters below.
|
||||
* Returns the changelog message, determined by the parameters below.
|
||||
* @param info The mod change info, containing the mod name and versions.
|
||||
* @param template The message template to replace in.
|
||||
* @param data The changelog data
|
||||
* @param commits The commits
|
||||
*/
|
||||
function getModChangeMessage(info: ModChangeInfo, template: string): string {
|
||||
function getModChangeMessage(
|
||||
info: ModChangeInfo,
|
||||
template: string,
|
||||
data: ChangelogData,
|
||||
commits: Commit[],
|
||||
): ChangelogMessage {
|
||||
const oldVersion = cleanupVersion(info.oldVersion);
|
||||
const newVersion = cleanupVersion(info.newVersion);
|
||||
|
||||
// If not provided with either version, return just mod name
|
||||
if (!oldVersion && !newVersion) return info.modName;
|
||||
if (!oldVersion && !newVersion)
|
||||
return {
|
||||
commitMessage: info.modName,
|
||||
specialFormatting: getModChangesFormatting(commits),
|
||||
};
|
||||
|
||||
// Replace in template
|
||||
return mustache.render(template, {
|
||||
let text = mustache.render(template, {
|
||||
modName: info.modName,
|
||||
oldVersion: oldVersion,
|
||||
newVersion: newVersion,
|
||||
});
|
||||
|
||||
// Parse Info
|
||||
let subMessages: ChangelogMessage[] = undefined;
|
||||
if (data.modInfoList.has(info.projectID)) {
|
||||
const modInfo = data.modInfoList.get(info.projectID);
|
||||
if (modInfo.info) text = `${text} ***(${modInfo.info})***`;
|
||||
if (modInfo.details)
|
||||
subMessages = modInfo.details.map((detail) => {
|
||||
detail.specialFormatting = getModChangesFormatting(commits);
|
||||
return detail;
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
commitMessage: text,
|
||||
specialFormatting: getModChangesFormatting(commits),
|
||||
subChangelogMessages: subMessages,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -174,15 +174,20 @@ export function sortCommitListReverse(list: Commit[]): void {
|
||||
* @return string Formatted Changelog Message
|
||||
*/
|
||||
async function formatChangelogMessage(changelogMessage: ChangelogMessage, subMessage = false): Promise<string> {
|
||||
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);
|
||||
|
@ -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<boolean> {
|
||||
if (!commit.body || !commit.body.includes(fixUpKey)) return false;
|
||||
await parseTOMLToList(
|
||||
await parseTOMLWithRootToList<FixUpInfo>(
|
||||
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<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a commit with 'mod info'.
|
||||
*/
|
||||
export async function parseModInfo(commitBody: string, commitObject: Commit): Promise<void> {
|
||||
await parseTOMLWithRootToList<ModInfo>(
|
||||
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<ParsedModInfo> {
|
||||
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<void> {
|
||||
await parseTOMLToList(
|
||||
await parseTOMLWithRootToList<ExpandedMessage>(
|
||||
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<ChangelogMessage[]> {
|
||||
const result: ChangelogMessage[] = [];
|
||||
await parseTOMLToList(
|
||||
await parseTOMLWithRootToList<string>(
|
||||
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<void> {
|
||||
await parseTOMLToList(
|
||||
await parseTOMLWithRootToList<string>(
|
||||
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<T>(
|
||||
delimiters: delimiter,
|
||||
engines: {
|
||||
toml: (input): Record<string, unknown> => {
|
||||
return toml.parse(input, "\n");
|
||||
return toml.parse(input);
|
||||
},
|
||||
},
|
||||
language: "toml",
|
||||
@ -314,50 +365,34 @@ async function parseTOML<T>(
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<T>(
|
||||
async function parseList<T>(
|
||||
messages: T[],
|
||||
listKey: string,
|
||||
commitBody: string,
|
||||
commitObject: Commit,
|
||||
delimiter: string,
|
||||
listKey: string,
|
||||
emptyCheck: (item: T) => string,
|
||||
perItemCallback: (item: T) => void,
|
||||
matterCallback?: (matter: GrayMatterFile<string>) => void,
|
||||
): Promise<void> {
|
||||
const messages = await parseTOML<T[]>(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<void>,
|
||||
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<T>(
|
||||
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<T>(
|
||||
commitBody: string,
|
||||
commitObject: Commit,
|
||||
delimiter: string,
|
||||
listKey: string,
|
||||
emptyCheck: (item: T) => boolean,
|
||||
perItemCallback: (item: T) => Promise<void>,
|
||||
rootObjTransform?: (root: Record<string, unknown>) => T,
|
||||
matterCallback?: (matter: GrayMatterFile<string>) => void,
|
||||
): Promise<void> {
|
||||
let root: Record<string, unknown>;
|
||||
const messages: T[] = [];
|
||||
|
||||
const endMessage = getEndMessage(delimiter);
|
||||
|
||||
// Parse Root TOML
|
||||
try {
|
||||
root = await parseTOML<Record<string, unknown>>(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<T>(messages, listKey, commitBody, commitObject, endMessage, emptyCheck, perItemCallback, (i) => i);
|
||||
}
|
||||
// Normal Parsing of List
|
||||
else await parseList<T>(list, listKey, commitBody, commitObject, endMessage, emptyCheck, perItemCallback);
|
||||
}
|
||||
|
||||
function getEndMessage(delimiter: string) {
|
||||
if (data.isTest) {
|
||||
return dedent`
|
||||
|
@ -122,8 +122,12 @@ export interface ChangelogMessage {
|
||||
export interface SpecialChangelogFormatting<T> {
|
||||
/**
|
||||
* 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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user