The commit parsing system provides types for integrating with conventional-commits-parser to parse and analyze commit message structure. This enables extracting structured data from commit messages for validation and processing.
type Parser = (message: string, options: Options) => Omit<Commit, "raw">;Parser function that converts a raw commit message string into a structured commit object:
The parsing types integrate directly with the conventional-commits-parser library:
import type { Commit, Options } from "conventional-commits-parser";import { Parser } from "@commitlint/types";
import { sync as parser } from "conventional-commits-parser";
// Using the parser type
const parseCommit: Parser = (message, options) => {
const parsed = parser(message, options);
// Remove raw property to match return type
const { raw, ...commit } = parsed;
return commit;
};
// Parse a commit message
const commitMessage = "feat(auth): add OAuth2 integration\n\nThis enables users to login with Google and GitHub.";
const parsedCommit = parseCommit(commitMessage, {
headerPattern: /^(\w*)(?:\(([\w$.\-* ]*)\))?: (.*)$/,
headerCorrespondence: ["type", "scope", "subject"]
});
console.log(parsedCommit);
// {
// type: "feat",
// scope: "auth",
// subject: "add OAuth2 integration",
// header: "feat(auth): add OAuth2 integration",
// body: "This enables users to login with Google and GitHub.",
// footer: null,
// notes: [],
// references: [],
// mentions: [],
// revert: null
// }import { Parser } from "@commitlint/types";
import type { Options } from "conventional-commits-parser";
// Custom parser options for different commit formats
const angularOptions: Options = {
headerPattern: /^(\w*)(?:\(([\w$.\-* ]*)\))?: (.*)$/,
headerCorrespondence: ["type", "scope", "subject"],
noteKeywords: ["BREAKING CHANGE"],
revertPattern: /^(?:Revert|revert:)\s"?([\s\S]+?)"?\s*This reverts commit (\w*)\./i,
revertCorrespondence: ["header", "hash"]
};
const conventionalCommitsOptions: Options = {
headerPattern: /^(\w*)(?:\((.*)\))?!?: (.*)$/,
headerCorrespondence: ["type", "scope", "subject"],
noteKeywords: ["BREAKING CHANGE", "BREAKING-CHANGE"],
issuePrefixes: ["#", "gh-"],
referenceActions: [
"close", "closes", "closed",
"fix", "fixes", "fixed",
"resolve", "resolves", "resolved"
]
};
// Parser factory
function createParser(options: Options): Parser {
return (message, parserOptions) => {
const mergedOptions = { ...options, ...parserOptions };
const parsed = parser(message, mergedOptions);
const { raw, ...commit } = parsed;
return commit;
};
}
const angularParser = createParser(angularOptions);
const conventionalParser = createParser(conventionalCommitsOptions);import { Parser, LintOptions } from "@commitlint/types";
import type { Options } from "conventional-commits-parser";
// Using parser in lint context
function createLintOptions(
customParser?: Parser,
parserOpts?: Options
): LintOptions {
return {
parserOpts: parserOpts || {
headerPattern: /^(\w*)(?:\(([\w$.\-* ]*)\))?: (.*)$/,
headerCorrespondence: ["type", "scope", "subject"],
noteKeywords: ["BREAKING CHANGE"]
},
defaultIgnores: true,
ignores: [
(commit) => commit.startsWith("Merge"),
(commit) => commit.startsWith("Revert")
]
};
}
// Example usage with custom parsing options
const lintOptions = createLintOptions(undefined, {
headerPattern: /^(\w+)(\(.+\))?: (.+)$/,
headerCorrespondence: ["type", "scope", "subject"],
fieldPattern: /^-(.*?)-$/,
noteKeywords: ["BREAKING CHANGE", "DEPRECATED"],
referenceActions: ["closes", "fixes", "resolves"]
});import { Parser } from "@commitlint/types";
import type { Commit } from "conventional-commits-parser";
// Analyzer using parsed commit structure
function analyzeCommit(parser: Parser, message: string, options: Options) {
const commit = parser(message, options);
return {
hasType: Boolean(commit.type),
hasScope: Boolean(commit.scope),
hasSubject: Boolean(commit.subject),
hasBody: Boolean(commit.body),
hasFooter: Boolean(commit.footer),
hasBreakingChanges: commit.notes?.some(note =>
note.title === "BREAKING CHANGE"
) || false,
references: commit.references?.length || 0,
mentions: commit.mentions?.length || 0,
isRevert: Boolean(commit.revert),
// Validation helpers
isValid: Boolean(commit.type && commit.subject),
isEmpty: !commit.type && !commit.subject && !commit.body,
// Structure summary
structure: {
header: commit.header,
components: {
type: commit.type,
scope: commit.scope,
subject: commit.subject
},
body: commit.body,
footer: commit.footer
}
};
}
// Usage
const analysis = analyzeCommit(
parseCommit,
"feat(api)!: add user authentication\n\nBREAKING CHANGE: API now requires auth tokens\n\nCloses #123",
angularOptions
);
console.log(analysis);
// {
// hasType: true,
// hasScope: true,
// hasSubject: true,
// hasBody: false,
// hasFooter: false,
// hasBreakingChanges: true,
// references: 1,
// mentions: 0,
// isRevert: false,
// isValid: true,
// isEmpty: false,
// structure: { ... }
// }import { Parser } from "@commitlint/types";
import type { Options } from "conventional-commits-parser";
// Parser that detects and handles multiple commit formats
class MultiFormatParser {
private parsers: Map<string, { parser: Parser; options: Options }> = new Map();
addFormat(name: string, parser: Parser, options: Options) {
this.parsers.set(name, { parser, options });
}
parse(message: string): { format: string; commit: ReturnType<Parser> } | null {
for (const [format, { parser, options }] of this.parsers) {
try {
const commit = parser(message, options);
// Check if parse was successful (has required components)
if (commit.type && commit.subject) {
return { format, commit };
}
} catch (error) {
// Continue to next parser
continue;
}
}
return null; // No parser succeeded
}
}
// Setup multi-format parser
const multiParser = new MultiFormatParser();
multiParser.addFormat("angular", parseCommit, angularOptions);
multiParser.addFormat("conventional", parseCommit, conventionalCommitsOptions);
multiParser.addFormat("custom", parseCommit, {
headerPattern: /^(\w+): (.+)$/,
headerCorrespondence: ["type", "subject"]
});
// Usage
const result = multiParser.parse("feat: add new feature");
if (result) {
console.log(`Detected format: ${result.format}`);
console.log("Parsed commit:", result.commit);
}import { Parser } from "@commitlint/types";
import type { Options, Commit } from "conventional-commits-parser";
// Validation wrapper for parser
function createValidatingParser(
baseParser: Parser,
validator: (commit: Omit<Commit, "raw">) => string[]
): Parser {
return (message, options) => {
const commit = baseParser(message, options);
const errors = validator(commit);
if (errors.length > 0) {
throw new Error(`Parse validation failed: ${errors.join(", ")}`);
}
return commit;
};
}
// Validation rules
function validateParsedCommit(commit: Omit<Commit, "raw">): string[] {
const errors: string[] = [];
if (!commit.type) {
errors.push("Missing commit type");
}
if (!commit.subject) {
errors.push("Missing commit subject");
}
if (commit.subject && commit.subject.length > 50) {
errors.push("Subject too long (max 50 characters)");
}
if (commit.type && !["feat", "fix", "docs", "style", "refactor", "test", "chore"].includes(commit.type)) {
errors.push(`Invalid commit type: ${commit.type}`);
}
return errors;
}
// Create validating parser
const validatingParser = createValidatingParser(parseCommit, validateParsedCommit);
// Usage
try {
const commit = validatingParser("invalid: this is a very long subject that exceeds the maximum allowed length", angularOptions);
console.log("Valid commit:", commit);
} catch (error) {
console.error("Parse error:", error.message);
}