@withstudiocms/internal_helpers
Version:
Internal helper utilities for StudioCMS
110 lines (109 loc) • 3.82 kB
JavaScript
import fs from "node:fs";
import { fromMarkdown } from "mdast-util-from-markdown";
import { toString as ToString } from "mdast-util-to-string";
import { visit } from "unist-util-visit";
const semverCategories = ["major", "minor", "patch"];
function loadChangelog(src) {
let markdown;
if ("path" in src) {
markdown = fs.readFileSync(src.path, "utf8");
} else {
markdown = src.raw;
}
markdown = markdown.replace(
/(?<=Thank[^.!]*? )@([a-z0-9-]+)(?=[\s,.!])/gi,
"[@$1](https://github.com/$1)"
);
const ast = fromMarkdown(markdown);
const changelog = {
packageName: "",
versions: []
};
let state = "packageName";
let version;
let semverCategory;
function handleNode(node) {
if (node.type === "heading") {
if (node.depth === 1) {
if (state !== "packageName") throw new Error("Unexpected h1");
changelog.packageName = ToString(node);
state = "version";
return;
}
if (node.depth === 2) {
if (state === "packageName") throw new Error("Unexpected h2");
version = {
version: ToString(node),
changes: {
major: { type: "list", children: [] },
minor: { type: "list", children: [] },
patch: { type: "list", children: [] }
},
includes: /* @__PURE__ */ new Set()
};
changelog.versions.push(version);
state = "semverCategory";
return;
}
if (node.depth === 3) {
if (state === "packageName" || state === "version") throw new Error("Unexpected h3");
semverCategory = (ToString(node).split(" ")[0] || "").toLowerCase();
if (!semverCategories.includes(semverCategory))
throw new Error(`Unexpected semver category: ${semverCategory}`);
state = "changes";
return;
}
}
if (node.type === "list") {
if (state !== "changes" || !version || !semverCategory) throw new Error("Unexpected list");
for (let listItemIdx = 0; listItemIdx < node.children.length; listItemIdx++) {
const listItem = node.children[listItemIdx];
if (!listItem) continue;
const lastChild = listItem.children[listItem.children.length - 1];
if (lastChild?.type === "list") {
const packageRefs = [];
lastChild.children.forEach((subListItem) => {
const text = ToString(subListItem);
if (parsePackageReference(text)) packageRefs.push(text);
});
if (packageRefs.length === lastChild.children.length) {
for (const packageRef of packageRefs) {
version.includes.add(packageRef);
}
listItem.children.pop();
}
}
const firstPara = listItem.children[0]?.type === "paragraph" ? listItem.children[0] : void 0;
if (firstPara) {
visit(firstPara, "text", (textNode) => {
textNode.value = textNode.value.replace(/(^[0-9a-f]{7,}: | \[[0-9a-f]{7,}\]$)/, "");
});
const firstParaText = ToString(firstPara);
if (firstParaText === "Updated dependencies") continue;
const packageRef = parsePackageReference(firstParaText);
if (packageRef) {
version.includes.add(firstParaText);
continue;
}
version.changes[semverCategory].children.push(listItem);
}
}
return;
}
throw new Error(`Unexpected node: ${JSON.stringify(node)}`);
}
ast.children.forEach((node) => {
handleNode(node);
});
return changelog;
}
function parsePackageReference(str) {
const matches = str.match(/^([@/a-z0-9-]+)@([0-9.]+)$/);
if (!matches) return;
const [, packageName, version] = matches;
return { packageName, version };
}
export {
loadChangelog,
semverCategories
};