Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions .changeset/changelog-formatter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
const { getInfo } = require("@changesets/get-github-info");

/**
* Custom changelog formatter for Plot project
* Follows "Keep a Changelog" standard with categories:
* Added, Changed, Deprecated, Removed, Fixed, Security
*
* Expects changeset summaries to be prefixed with category, e.g.:
* "Added: new feature"
* "Fixed: bug description"
*/

const CATEGORIES = [
"Added",
"Changed",
"Deprecated",
"Removed",
"Fixed",
"Security"
];

const CATEGORY_PATTERN = new RegExp(`^(${CATEGORIES.join("|")}):\\s*`, "i");

/**
* Parse a changeset summary to extract category and content
*/
function parseChangeset(summary) {
const match = summary.match(CATEGORY_PATTERN);

if (match) {
const category = match[1].charAt(0).toUpperCase() + match[1].slice(1).toLowerCase();
const content = summary.slice(match[0].length);
return { category, content };
}

// Default to "Changed" if no category prefix found
return { category: "Changed", content: summary };
}

/**
* Format a single release line for a changeset
*/
async function getReleaseLine(changeset, type, options) {
if (!options || !options.repo) {
throw new Error("Must provide options.repo for changelog-formatter");
}

const { summary } = changeset;
const { category, content } = parseChangeset(summary);

let links = "";

try {
const info = await getInfo({
repo: options.repo,
commit: changeset.commit,
});

// Format: [#PR](link) [`hash`](link)
const prLink = info.pull ? `[#${info.pull}](${info.links.pull})` : null;
const commitLink = info.commit ? `[\`${info.commit.slice(0, 7)}\`](${info.links.commit})` : null;

const linkParts = [prLink, commitLink].filter(Boolean);
if (linkParts.length > 0) {
links = ` (${linkParts.join(" ")})`;
}
} catch (error) {
// If we can't get GitHub info, just continue without links
console.warn("Could not get GitHub info for changeset:", error.message);
}

// Include category as HTML comment for post-processing
return `<!-- CATEGORY:${category} -->${content}${links}`;
}

/**
* Format dependency update lines
*/
async function getDependencyReleaseLine(changesets, dependenciesUpdated, options) {
if (changesets.length === 0) return "";

const updatedDependencies = dependenciesUpdated.map(
(dependency) => ` - ${dependency.name}@${dependency.newVersion}`
);

return ["<!-- CATEGORY:Changed -->Updated dependencies:", ...updatedDependencies].join("\n");
}

module.exports = {
getReleaseLine,
getDependencyReleaseLine,
};
2 changes: 1 addition & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
"changelog": ["@changesets/changelog-github", { "repo": "plotday/plot" }],
"changelog": ["./.changeset/changelog-formatter.js", { "repo": "plotday/plot" }],
"commit": false,
"fixed": [],
"linked": [],
Expand Down
8 changes: 8 additions & 0 deletions .changeset/fruity-horses-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@plotday/sdk": patch
"@plotday/tool-google-calendar": patch
"@plotday/tool-google-contacts": patch
"@plotday/tool-outlook-calendar": patch
---

Changed: improved changelog format
160 changes: 160 additions & 0 deletions .changeset/reorganize-changelogs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
#!/usr/bin/env node

/**
* Post-processing script to reorganize CHANGELOG.md files
* Converts changesets' default grouping (Major/Minor/Patch Changes)
* into "Keep a Changelog" format (Added/Changed/Fixed/etc.)
*/

const fs = require("fs");
const path = require("path");

const CATEGORIES = [
"Added",
"Changed",
"Deprecated",
"Removed",
"Fixed",
"Security"
];

/**
* Process a single CHANGELOG.md file
*/
function processChangelog(filePath) {
const content = fs.readFileSync(filePath, "utf-8");
const lines = content.split("\n");

let result = [];
let i = 0;

while (i < lines.length) {
const line = lines[i];

// Check if this is a version header (e.g., "## 1.0.0")
if (line.match(/^##\s+\d+\.\d+\.\d+/)) {
result.push(line);
i++;

// Skip empty lines after version header
while (i < lines.length && lines[i].trim() === "") {
i++;
}

// Check if we have change type sections (Minor Changes, Patch Changes, etc.)
const sectionStart = i;
const changes = [];

// Collect all changes in this version
let currentCategory = null;

while (i < lines.length && !lines[i].match(/^##\s+\d+\.\d+\.\d+/)) {
const currentLine = lines[i];

// Check for "Keep a Changelog" category headers (already formatted)
const keepAChangelogMatch = currentLine.match(/^###\s+(Added|Changed|Deprecated|Removed|Fixed|Security)$/);
if (keepAChangelogMatch) {
currentCategory = keepAChangelogMatch[1];
i++;
continue;
}

// Skip the old "### Minor Changes", "### Patch Changes" headers
if (currentLine.match(/^###\s+(Major|Minor|Patch)\s+Changes/)) {
currentCategory = null; // Reset category for old format
i++;
continue;
}

// Check for category markers in the content (from changelog formatter)
const categoryMatch = currentLine.match(/<!--\s*CATEGORY:(\w+)\s*-->(.*)/);
if (categoryMatch) {
const category = categoryMatch[1];
const content = categoryMatch[2];
changes.push({ category, content: `- ${content}` });
i++;
} else if (currentLine.trim() !== "" && currentLine.match(/^-\s+/)) {
// If we have a current category (from Keep a Changelog format), use it
// Otherwise, treat as "Changed" (legacy/old format)
const category = currentCategory || "Changed";
const contentLines = [currentLine];
i++;

// Collect any indented continuation lines (e.g., for dependency lists)
while (i < lines.length && lines[i].match(/^ /)) {
contentLines.push(lines[i]);
i++;
}

changes.push({ category, content: contentLines.join("\n") });
} else {
i++;
}
}

// Group changes by category and output in "Keep a Changelog" order
if (changes.length > 0) {
result.push(""); // Empty line after version header

for (const category of CATEGORIES) {
const categoryChanges = changes.filter(c => c.category === category);
if (categoryChanges.length > 0) {
result.push(`### ${category}`);
result.push("");
categoryChanges.forEach(change => {
result.push(change.content);
});
result.push("");
}
}
}
} else {
// Keep non-version lines as-is (like the package name header)
result.push(line);
i++;
}
}

// Write the reorganized content back
fs.writeFileSync(filePath, result.join("\n"));
console.log(`Reorganized: ${filePath}`);
}

/**
* Recursively find CHANGELOG.md files
*/
function findChangelogFiles(dir, fileList = []) {
const files = fs.readdirSync(dir);

files.forEach(file => {
const filePath = path.join(dir, file);
const stat = fs.statSync(filePath);

if (stat.isDirectory()) {
// Skip node_modules and hidden directories
if (file !== "node_modules" && !file.startsWith(".")) {
findChangelogFiles(filePath, fileList);
}
} else if (file === "CHANGELOG.md") {
fileList.push(filePath);
}
});

return fileList;
}

/**
* Find and process all CHANGELOG.md files
*/
function main() {
const rootDir = path.join(__dirname, "..");
const changelogFiles = findChangelogFiles(rootDir);

console.log(`Found ${changelogFiles.length} changelog files`);

changelogFiles.forEach(processChangelog);

console.log("Done reorganizing changelogs!");
}

main();
Loading