Utility functions for commit transformation, date formatting, JSON serialization, and sorting operations. These functions provide the foundational operations used throughout the changelog generation process.
Apply transformation to commit with immutable proxy protection. This ensures commits cannot be modified during transformation and provides a safe way to apply custom transformations.
/**
* Apply transformation to commit with immutable proxy protection.
* @param commit - The commit object to transform
* @param transform - Transformation function or null/undefined to skip transformation
* @param args - Additional arguments passed to the transformation function
* @returns Promise resolving to transformed commit or null if filtered out
*/
function transformCommit<Commit extends AnyObject, Args extends unknown[]>(
commit: Commit,
transform: ((commit: Commit, ...args: Args) => Partial<Commit> | null | Promise<Partial<Commit> | null>) | null | undefined,
...args: Args
): Promise<TransformedCommit<Commit> | null>;Usage Examples:
import { transformCommit, type CommitKnownProps } from "conventional-changelog-writer";
const commit: CommitKnownProps = {
hash: "abcdef1234567890",
header: "feat(auth): add OAuth2 support with extensive configuration options",
type: "feat",
version: null,
committerDate: "2023-01-15T10:30:00Z",
notes: [],
body: null,
footer: null
};
// Custom transformation function
const customTransform = (commit, context, options) => {
return {
shortHash: commit.hash?.substring(0, 7),
truncatedHeader: commit.header?.substring(0, 50),
formattedDate: new Date(commit.committerDate).toLocaleDateString()
};
};
// Apply transformation
const transformed = await transformCommit(commit, customTransform, context, options);
console.log(transformed);
// Result: {
// ...originalCommit,
// shortHash: "abcdef1",
// truncatedHeader: "feat(auth): add OAuth2 support with extensive conf",
// formattedDate: "1/15/2023",
// raw: originalCommit
// }
// Filter out commits (return null to exclude)
const filterTransform = (commit) => {
return commit.type === 'docs' ? null : commit;
};
const filtered = await transformCommit(commit, filterTransform);
// Returns null for docs commits, original commit for othersDefault commit transform function that truncates hash and header, and formats dates. This is the built-in transformation applied when no custom transform is provided.
/**
* Default commit transform function.
* @param commit - The commit to transform
* @param _context - Context object (unused in default transform)
* @param options - Options containing formatDate function
* @returns Partial commit object with transformed properties
*/
function defaultCommitTransform<Commit extends CommitKnownProps = CommitKnownProps>(
commit: Commit,
_context: unknown,
options: Pick<FinalOptions<Commit>, 'formatDate'>
): Partial<Commit>;Usage Example:
import { defaultCommitTransform, formatDate } from "conventional-changelog-writer";
const commit = {
hash: "abcdef1234567890abcdef1234567890abcdef12",
header: "feat(authentication): implement comprehensive OAuth2 support with multiple providers and extensive configuration options",
committerDate: "2023-01-15T10:30:00Z",
type: "feat",
notes: [],
body: null,
footer: null
};
const options = { formatDate };
const transformed = defaultCommitTransform(commit, null, options);
console.log(transformed);
// Result:
// {
// hash: "abcdef1", // Truncated to 7 characters
// header: "feat(authentication): implement comprehensive OAuth2 support with multiple providers and", // Truncated to 100 chars
// committerDate: "2023-01-15" // Formatted as yyyy-mm-dd
// }Formats date to yyyy-mm-dd format for consistent date representation in changelogs.
/**
* Formats date to yyyy-mm-dd format.
* @param date - Date string or Date object to format
* @returns Date string in yyyy-mm-dd format
*/
function formatDate(date: string | Date): string;Usage Examples:
import { formatDate } from "conventional-changelog-writer";
// Format Date object
const dateObj = new Date("2023-01-15T10:30:00Z");
console.log(formatDate(dateObj)); // "2023-01-15"
// Format date string
console.log(formatDate("2023-01-15T10:30:00Z")); // "2023-01-15"
console.log(formatDate("January 15, 2023")); // "2023-01-15"
// Use in options
const options = {
formatDate: formatDate,
transform: (commit, context, options) => ({
formattedDate: options.formatDate(commit.committerDate)
})
};Safe JSON.stringify with circular reference support. Handles circular references gracefully by replacing them with descriptive strings.
/**
* Safe JSON.stringify with circular reference support.
* @param obj - Object to stringify (can contain circular references)
* @returns Stringified object with circular references replaced
*/
function stringify(obj: unknown): string;Usage Examples:
import { stringify } from "conventional-changelog-writer";
// Handle circular references
const obj = { name: "test" };
obj.self = obj; // Create circular reference
console.log(stringify(obj));
// Result: '{\n "name": "test",\n "self": "[Circular ~]"\n}'
// Handle complex objects
const commit = {
hash: "abc123",
header: "feat: add feature",
metadata: {
processed: true,
refs: []
}
};
commit.metadata.refs.push(commit); // Circular reference
const serialized = stringify(commit);
console.log(serialized); // Safe serialization with circular refs handled
// Debug usage (this is used internally for debug output)
const debugInfo = {
context: finalContext,
options: finalOptions,
commits: processedCommits
};
console.log(`Debug info:\n${stringify(debugInfo)}`);Creates a compare function for sorting from object keys. Supports single keys, multiple keys, or custom comparator functions.
/**
* Creates a compare function for sorting from object keys.
* @param strings - String key, array of string keys, existing comparator function, or undefined
* @returns Comparator function for use with Array.sort()
*/
function createComparator<K extends string, T extends StringsRecord<K>>(
strings: K | K[] | Comparator<T> | undefined
): Comparator<T> | undefined;Usage Examples:
import { createComparator } from "conventional-changelog-writer";
interface Commit {
type: string;
scope?: string;
subject: string;
date: string;
}
const commits: Commit[] = [
{ type: "feat", scope: "auth", subject: "add login", date: "2023-01-15" },
{ type: "fix", scope: "auth", subject: "fix logout", date: "2023-01-14" },
{ type: "feat", scope: "ui", subject: "add button", date: "2023-01-16" }
];
// Sort by single field
const typeComparator = createComparator("type");
commits.sort(typeComparator);
// Result: fix items first, then feat items
// Sort by multiple fields
const multiComparator = createComparator(["type", "scope", "subject"]);
commits.sort(multiComparator);
// Result: sorted by type, then scope, then subject
// Use with existing comparator (pass-through)
const customComparator = (a: Commit, b: Commit) =>
new Date(b.date).getTime() - new Date(a.date).getTime();
const passedComparator = createComparator(customComparator);
// Returns the same function
// Use in options
const options = {
commitsSort: createComparator(["scope", "subject"]),
commitGroupsSort: createComparator("title")
};import { transformCommit, formatDate } from "conventional-changelog-writer";
// Multi-stage transformation
async function processCommit(commit: CommitKnownProps) {
// Stage 1: Apply default formatting
const defaultTransformed = await transformCommit(
commit,
defaultCommitTransform,
null,
{ formatDate }
);
if (!defaultTransformed) return null;
// Stage 2: Apply custom business logic
const customTransformed = await transformCommit(
defaultTransformed,
(commit) => ({
priority: commit.type === 'feat' ? 'high' : 'normal',
category: categorizeCommit(commit),
hasBreakingChanges: commit.notes.some(note =>
note.title === 'BREAKING CHANGE'
)
})
);
return customTransformed;
}
function categorizeCommit(commit: CommitKnownProps): string {
if (commit.type === 'feat') return 'Features';
if (commit.type === 'fix') return 'Bug Fixes';
if (commit.type === 'docs') return 'Documentation';
return 'Other';
}The transformCommit function uses a Proxy to prevent accidental modifications:
const transform = (commit) => {
// This will throw an error - commits are immutable during transformation
// commit.hash = "modified"; // Error: Cannot modify immutable object
// Instead, return a patch object
return {
modifiedHash: commit.hash + "-modified"
};
};