Comprehensive rule system with 35 built-in rules covering all aspects of conventional commit messages. Rules are configurable with severity levels and conditions.
All rules follow a consistent configuration tuple format with severity levels and conditions.
// Rule configuration tuple format
type RuleConfigTuple<T> =
| Readonly<[RuleConfigSeverity.Disabled]> // [0] - Disable rule
| Readonly<[RuleConfigSeverity, RuleConfigCondition]> // [level, when]
| Readonly<[RuleConfigSeverity, RuleConfigCondition, T]> // [level, when, value]
enum RuleConfigSeverity {
Disabled = 0, // Rule is disabled
Warning = 1, // Rule produces warnings
Error = 2, // Rule produces errors
}
type RuleConfigCondition = 'always' | 'never';
// Configuration examples
{
'type-empty': [2, 'never'], // Error if type is empty
'subject-max-length': [2, 'always', 72], // Error if subject > 72 chars
'scope-case': [1, 'always', 'kebab-case'], // Warning if scope not kebab-case
'body-leading-blank': [0] // Disable rule
}Rules for validating the commit type (feat, fix, docs, etc.).
// Type case validation
'type-case': [RuleConfigSeverity, RuleConfigCondition, TargetCaseType];
// Type presence validation
'type-empty': [RuleConfigSeverity, RuleConfigCondition];
// Type enumeration validation
'type-enum': [RuleConfigSeverity, RuleConfigCondition, string[]];
// Type length validation
'type-max-length': [RuleConfigSeverity, RuleConfigCondition, number];
'type-min-length': [RuleConfigSeverity, RuleConfigCondition, number];
type TargetCaseType =
| 'camel-case' | 'kebab-case' | 'snake-case' | 'pascal-case'
| 'upper-case' | 'lower-case' | 'sentence-case' | 'start-case';Usage Examples:
module.exports = {
rules: {
'type-case': [2, 'always', 'lower-case'], // Type must be lowercase
'type-empty': [2, 'never'], // Type cannot be empty
'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore']],
'type-max-length': [2, 'always', 10], // Type max 10 characters
'type-min-length': [2, 'always', 3] // Type min 3 characters
}
};Rules for validating the commit scope (api, ui, core, etc.).
// Scope case validation
'scope-case': [RuleConfigSeverity, RuleConfigCondition, TargetCaseType];
// Scope presence validation
'scope-empty': [RuleConfigSeverity, RuleConfigCondition];
// Scope enumeration validation
'scope-enum': [RuleConfigSeverity, RuleConfigCondition, string[]];
// Scope length validation
'scope-max-length': [RuleConfigSeverity, RuleConfigCondition, number];
'scope-min-length': [RuleConfigSeverity, RuleConfigCondition, number];Usage Examples:
module.exports = {
rules: {
'scope-case': [2, 'always', 'kebab-case'], // Scope must be kebab-case
'scope-empty': [1, 'never'], // Warn if scope is empty
'scope-enum': [2, 'always', ['api', 'ui', 'core', 'docs', 'ci']], // Allowed scopes
'scope-max-length': [2, 'always', 20], // Scope max 20 characters
'scope-min-length': [2, 'always', 2] // Scope min 2 characters
}
};Rules for validating the commit subject/description.
// Subject case validation
'subject-case': [RuleConfigSeverity, RuleConfigCondition, TargetCaseType];
// Subject presence validation
'subject-empty': [RuleConfigSeverity, RuleConfigCondition];
// Subject punctuation validation
'subject-full-stop': [RuleConfigSeverity, RuleConfigCondition, string];
// Subject exclamation mark validation
'subject-exclamation-mark': [RuleConfigSeverity, RuleConfigCondition];
// Subject length validation
'subject-max-length': [RuleConfigSeverity, RuleConfigCondition, number];
'subject-min-length': [RuleConfigSeverity, RuleConfigCondition, number];Usage Examples:
module.exports = {
rules: {
'subject-case': [2, 'always', 'sentence-case'], // Subject must be sentence case
'subject-empty': [2, 'never'], // Subject cannot be empty
'subject-full-stop': [2, 'never', '.'], // Subject should not end with period
'subject-exclamation-mark': [1, 'never'], // Warn on exclamation marks
'subject-max-length': [2, 'always', 72], // Subject max 72 characters
'subject-min-length': [2, 'always', 10] // Subject min 10 characters
}
};Rules for validating the entire commit message header.
// Header case validation
'header-case': [RuleConfigSeverity, RuleConfigCondition, TargetCaseType];
// Header punctuation validation
'header-full-stop': [RuleConfigSeverity, RuleConfigCondition, string];
// Header length validation
'header-max-length': [RuleConfigSeverity, RuleConfigCondition, number];
'header-min-length': [RuleConfigSeverity, RuleConfigCondition, number];
// Header whitespace validation
'header-trim': [RuleConfigSeverity, RuleConfigCondition];Usage Examples:
module.exports = {
rules: {
'header-case': [2, 'always', 'lower-case'], // Header must be lowercase
'header-full-stop': [2, 'never', '.'], // Header should not end with period
'header-max-length': [2, 'always', 100], // Header max 100 characters
'header-min-length': [2, 'always', 15], // Header min 15 characters
'header-trim': [2, 'always'] // Header should be trimmed
}
};Rules for validating the commit message body.
// Body case validation
'body-case': [RuleConfigSeverity, RuleConfigCondition, TargetCaseType];
// Body presence validation
'body-empty': [RuleConfigSeverity, RuleConfigCondition];
// Body punctuation validation
'body-full-stop': [RuleConfigSeverity, RuleConfigCondition, string];
// Body formatting validation
'body-leading-blank': [RuleConfigSeverity, RuleConfigCondition];
// Body length validation
'body-max-length': [RuleConfigSeverity, RuleConfigCondition, number];
'body-max-line-length': [RuleConfigSeverity, RuleConfigCondition, number];
'body-min-length': [RuleConfigSeverity, RuleConfigCondition, number];Usage Examples:
module.exports = {
rules: {
'body-case': [1, 'always', 'sentence-case'], // Warn if body not sentence case
'body-empty': [1, 'never'], // Warn if body is empty
'body-full-stop': [2, 'always', '.'], // Body should end with period
'body-leading-blank': [2, 'always'], // Body should have leading blank line
'body-max-length': [2, 'always', 500], // Body max 500 characters
'body-max-line-length': [2, 'always', 80], // Body line max 80 characters
'body-min-length': [2, 'always', 20] // Body min 20 characters
}
};Rules for validating the commit message footer.
// Footer presence validation
'footer-empty': [RuleConfigSeverity, RuleConfigCondition];
// Footer formatting validation
'footer-leading-blank': [RuleConfigSeverity, RuleConfigCondition];
// Footer length validation
'footer-max-length': [RuleConfigSeverity, RuleConfigCondition, number];
'footer-max-line-length': [RuleConfigSeverity, RuleConfigCondition, number];
'footer-min-length': [RuleConfigSeverity, RuleConfigCondition, number];Usage Examples:
module.exports = {
rules: {
'footer-empty': [1, 'never'], // Warn if footer is empty
'footer-leading-blank': [2, 'always'], // Footer should have leading blank line
'footer-max-length': [2, 'always', 300], // Footer max 300 characters
'footer-max-line-length': [2, 'always', 80], // Footer line max 80 characters
'footer-min-length': [2, 'always', 10] // Footer min 10 characters
}
};Rules for validating issue references and Git trailers.
// Issue reference validation
'references-empty': [RuleConfigSeverity, RuleConfigCondition];
// Git trailer validation
'signed-off-by': [RuleConfigSeverity, RuleConfigCondition, string];
'trailer-exists': [RuleConfigSeverity, RuleConfigCondition, string];Usage Examples:
module.exports = {
rules: {
'references-empty': [1, 'never'], // Warn if no issue references
'signed-off-by': [2, 'always', 'Signed-off-by'], // Require signed-off-by trailer
'trailer-exists': [2, 'always', 'Reviewed-by'] // Require reviewed-by trailer
}
};Understanding how rules are applied and validated.
// Condition behavior
'always' // Rule applies when the condition is met
'never' // Rule applies when the condition is NOT met
// Examples:
'type-empty': [2, 'never'] // Error when type IS empty
'type-empty': [2, 'always'] // Error when type IS NOT empty
'body-leading-blank': [2, 'always'] // Error when there IS NOT a leading blank
'body-leading-blank': [2, 'never'] // Error when there IS a leading blank// Comprehensive rule configuration
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
// Type rules
'type-case': [2, 'always', 'lower-case'],
'type-empty': [2, 'never'],
'type-enum': [2, 'always', [
'build', 'chore', 'ci', 'docs', 'feat',
'fix', 'perf', 'refactor', 'revert', 'style', 'test'
]],
'type-max-length': [2, 'always', 10],
'type-min-length': [2, 'always', 3],
// Scope rules
'scope-case': [2, 'always', 'kebab-case'],
'scope-empty': [1, 'never'],
'scope-enum': [2, 'always', [
'api', 'ui', 'core', 'docs', 'ci', 'build', 'test'
]],
'scope-max-length': [2, 'always', 15],
// Subject rules
'subject-case': [2, 'always', 'sentence-case'],
'subject-empty': [2, 'never'],
'subject-full-stop': [2, 'never', '.'],
'subject-max-length': [2, 'always', 72],
'subject-min-length': [2, 'always', 10],
// Header rules
'header-max-length': [2, 'always', 100],
'header-trim': [2, 'always'],
// Body rules
'body-leading-blank': [2, 'always'],
'body-max-line-length': [2, 'always', 80],
'body-case': [1, 'always', 'sentence-case'],
// Footer rules
'footer-leading-blank': [2, 'always'],
'footer-max-line-length': [2, 'always', 80],
// Other rules
'references-empty': [1, 'never'],
'signed-off-by': [2, 'always', 'Signed-off-by']
}
};Creating custom validation rules.
// Custom rule function signature
function customRule(parsed, when, value) {
// parsed: Parsed commit object
// when: 'always' | 'never'
// value: Rule configuration value
// Return [isValid, message]
const isValid = /* validation logic */;
const message = isValid ? '' : 'Custom validation error message';
return [isValid, message];
}
// Plugin with custom rules
const customPlugin = {
rules: {
'custom-type-prefix': (parsed, when, prefix) => {
const hasPrefix = parsed.type && parsed.type.startsWith(prefix);
const valid = when === 'always' ? hasPrefix : !hasPrefix;
const message = valid ? '' : `Type should ${when === 'always' ? 'start' : 'not start'} with "${prefix}"`;
return [valid, message];
}
}
};
module.exports = {
plugins: [customPlugin],
rules: {
'custom-type-prefix': [2, 'always', 'feat-']
}
};Testing rule configurations and validation.
# Test specific commit message
echo "feat(api): add user authentication" | commitlint
# Test with specific rules
echo "invalid commit" | commitlint --config test-rules.js
# Verbose output for rule debugging
echo "fix: bug fix" | commitlint --verbose
# Print applied rules
commitlint --print-config json | jq '.rules'