Flexible configuration system supporting multiple formats, shareable presets, and extensible rules. Includes built-in configurations for popular project types and development workflows.
Commitlint supports multiple configuration file formats with automatic discovery.
// commitlint.config.js (recommended)
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore']],
'subject-max-length': [2, 'always', 72]
}
};
// .commitlintrc.js
module.exports = { /* config */ };
// .commitlintrc.json
{
"extends": ["@commitlint/config-conventional"],
"rules": { /* rules */ }
}
// .commitlintrc.yml
extends:
- '@commitlint/config-conventional'
rules:
type-enum: [2, 'always', ['feat', 'fix']]
// package.json
{
"commitlint": {
"extends": ["@commitlint/config-conventional"]
}
}Complete configuration interface with all supported options.
interface UserConfig {
extends?: string | string[]; // Shareable configurations to extend
formatter?: string; // Custom formatter module
rules?: Partial<RulesConfig>; // Rule configuration object
parserPreset?: string | ParserPreset | Promise<ParserPreset>; // Parser preset
ignores?: ((commit: string) => boolean)[]; // Custom ignore functions
defaultIgnores?: boolean; // Use default ignores (default: true)
plugins?: (string | Plugin)[]; // Plugin modules
helpUrl?: string; // Help URL for error messages
prompt?: UserPromptConfig; // Interactive prompt configuration
}
interface ParserPreset {
name?: string; // Preset name
path?: string; // Preset path
parserOpts?: ParserOptions; // Parser options
}
interface ParserOptions {
commentChar?: string; // Comment character (default: #)
fieldPattern?: RegExp; // Field parsing pattern
headerCorrespondence?: string[]; // Header field correspondence
headerPattern?: RegExp; // Header parsing pattern
issuePrefixes?: string[]; // Issue prefixes (#, etc.)
mergeCorrespondence?: string[]; // Merge field correspondence
mergePattern?: RegExp; // Merge parsing pattern
noteKeywords?: string[]; // Keywords for notes (BREAKING CHANGE, etc.)
revertCorrespondence?: string[]; // Revert field correspondence
revertPattern?: RegExp; // Revert parsing pattern
}Ready-to-use configuration presets for popular project types and conventions.
// @commitlint/config-conventional - Standard conventional commits
{
extends: ['@commitlint/config-conventional']
}
// @commitlint/config-angular - Angular commit convention
{
extends: ['@commitlint/config-angular']
}
// @commitlint/config-lerna-scopes - Lerna workspace scopes
{
extends: ['@commitlint/config-lerna-scopes']
}
// @commitlint/config-nx-scopes - Nx workspace scopes
{
extends: ['@commitlint/config-nx-scopes']
}
// Multiple presets
{
extends: [
'@commitlint/config-conventional',
'@commitlint/config-lerna-scopes'
]
}Usage Examples:
# Install conventional config
npm install --save-dev @commitlint/config-conventional
# Install Angular config
npm install --save-dev @commitlint/config-angular
# Install Lerna scopes config
npm install --save-dev @commitlint/config-lerna-scopesConfigure validation rules with severity levels and conditions.
// Rule configuration tuple format
type RuleConfigTuple<T> =
| Readonly<[RuleConfigSeverity.Disabled]> // Disable rule
| Readonly<[RuleConfigSeverity, RuleConfigCondition]> // Severity + condition
| Readonly<[RuleConfigSeverity, RuleConfigCondition, T]> // Severity + condition + value
enum RuleConfigSeverity {
Disabled = 0, // Rule is disabled
Warning = 1, // Rule produces warnings
Error = 2, // Rule produces errors
}
type RuleConfigCondition = 'always' | 'never';
// Rule configuration object
interface RulesConfig {
// Type rules
'type-case': RuleConfigTuple<TargetCaseType>;
'type-empty': RuleConfigTuple<void>;
'type-enum': RuleConfigTuple<string[]>;
'type-max-length': RuleConfigTuple<number>;
'type-min-length': RuleConfigTuple<number>;
// Scope rules
'scope-case': RuleConfigTuple<TargetCaseType>;
'scope-empty': RuleConfigTuple<void>;
'scope-enum': RuleConfigTuple<string[]>;
'scope-max-length': RuleConfigTuple<number>;
'scope-min-length': RuleConfigTuple<number>;
// Subject rules
'subject-case': RuleConfigTuple<TargetCaseType>;
'subject-empty': RuleConfigTuple<void>;
'subject-full-stop': RuleConfigTuple<string>;
'subject-max-length': RuleConfigTuple<number>;
'subject-min-length': RuleConfigTuple<number>;
'subject-exclamation-mark': RuleConfigTuple<void>;
// Header rules
'header-case': RuleConfigTuple<TargetCaseType>;
'header-full-stop': RuleConfigTuple<string>;
'header-max-length': RuleConfigTuple<number>;
'header-min-length': RuleConfigTuple<number>;
'header-trim': RuleConfigTuple<void>;
// Body rules
'body-case': RuleConfigTuple<TargetCaseType>;
'body-empty': RuleConfigTuple<void>;
'body-full-stop': RuleConfigTuple<string>;
'body-leading-blank': RuleConfigTuple<void>;
'body-max-length': RuleConfigTuple<number>;
'body-max-line-length': RuleConfigTuple<number>;
'body-min-length': RuleConfigTuple<number>;
// Footer rules
'footer-empty': RuleConfigTuple<void>;
'footer-leading-blank': RuleConfigTuple<void>;
'footer-max-length': RuleConfigTuple<number>;
'footer-max-line-length': RuleConfigTuple<number>;
'footer-min-length': RuleConfigTuple<number>;
// Other rules
'references-empty': RuleConfigTuple<void>;
'signed-off-by': RuleConfigTuple<string>;
'trailer-exists': RuleConfigTuple<string>;
}
type TargetCaseType =
| 'camel-case'
| 'kebab-case'
| 'snake-case'
| 'pascal-case'
| 'upper-case'
| 'lower-case'
| 'sentence-case'
| 'start-case';Configuration Examples:
// Basic rule configuration
module.exports = {
rules: {
'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore']],
'type-case': [2, 'always', 'lower-case'],
'type-empty': [2, 'never'],
'scope-case': [2, 'always', 'lower-case'],
'subject-case': [2, 'always', 'sentence-case'],
'subject-empty': [2, 'never'],
'subject-max-length': [2, 'always', 72],
'header-max-length': [2, 'always', 100],
'body-leading-blank': [2, 'always'],
'footer-leading-blank': [2, 'always']
}
};
// Disable specific rules
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'scope-empty': [0], // Disable scope-empty rule
'subject-case': [1, 'always', 'lower-case'], // Warning instead of error
'body-max-line-length': [2, 'always', 80] // Custom line length
}
};Configure commit message parsing behavior.
// Using string preset name
module.exports = {
parserPreset: 'conventional-changelog-conventionalcommits'
};
// Using preset object
module.exports = {
parserPreset: {
name: 'conventional-changelog-conventionalcommits',
parserOpts: {
noteKeywords: ['BREAKING CHANGE', 'BREAKING CHANGES', 'DEPRECATED'],
referenceActions: ['close', 'closes', 'closed', 'fix', 'fixes', 'fixed', 'resolve', 'resolves', 'resolved'],
issuePrefixes: ['#', 'gh-'],
noteKeywords: ['BREAKING CHANGE', 'BREAKING CHANGES']
}
}
};
// Custom parser preset
module.exports = {
parserPreset: {
parserOpts: {
headerPattern: /^(\w*)(?:\((.*)\))?: (.*)$/,
headerCorrespondence: ['type', 'scope', 'subject'],
noteKeywords: ['BREAKING CHANGE', 'BREAKING-CHANGE'],
revertPattern: /^(?:Revert|revert:)\s"?([\s\S]+?)"?\s*This reverts commit (\w*)\./i,
revertCorrespondence: ['header', 'hash']
}
}
};Configure which commits should be ignored during linting.
module.exports = {
// Custom ignore functions
ignores: [
(message) => message.includes('[skip]'),
(message) => message.includes('WIP:'),
(message) => /^Merge branch/.test(message),
(message) => /^Revert/.test(message)
],
// Disable default ignores
defaultIgnores: false // Default: true
};
// Default ignores include:
// - Merge commits
// - Revert commits
// - Fixup commits
// - Squash commitsExtend commitlint with custom plugins.
// Using plugin modules
module.exports = {
plugins: [
'commitlint-plugin-function-rules',
'@commitlint/plugin-help-wanted'
],
rules: {
'function-rules/type-enum': [2, 'always', ['feat', 'fix']]
}
};
// Custom plugin object
const customPlugin = {
rules: {
'custom-rule': (parsed, when, value) => {
// Custom rule implementation
return [true, 'Custom rule message'];
}
}
};
module.exports = {
plugins: [customPlugin],
rules: {
'custom-rule': [2, 'always', 'some-value']
}
};Configure output formatting.
module.exports = {
formatter: '@commitlint/format', // Default formatter
// or custom formatter
formatter: './custom-formatter.js'
};
// Custom formatter implementation
module.exports = (report, options) => {
const {results} = report;
return results
.map(result => `${result.valid ? 'β' : 'β'} ${result.input}`)
.join('\n');
};Configure commitlint for different environments.
const isCI = process.env.CI === 'true';
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'subject-max-length': [2, 'always', isCI ? 50 : 72],
'body-max-line-length': [isCI ? 2 : 1, 'always', 80]
},
helpUrl: isCI
? 'https://ci-docs.example.com/commit-guidelines'
: 'https://docs.example.com/commit-guidelines'
};Configure commitlint for monorepo projects.
// Lerna configuration
module.exports = {
extends: [
'@commitlint/config-conventional',
'@commitlint/config-lerna-scopes'
]
};
// Nx configuration
module.exports = {
extends: [
'@commitlint/config-conventional',
'@commitlint/config-nx-scopes'
]
};
// Custom scope configuration
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'scope-enum': [2, 'always', [
'api',
'web',
'mobile',
'shared',
'docs',
'ci'
]]
}
};Configure interactive commit message prompting.
module.exports = {
prompt: {
messages: {
skip: ':skip',
max: 'upper %d chars',
min: '%d chars at least',
emptyWarning: 'can not be empty',
upperLimitWarning: 'over limit',
lowerLimitWarning: 'below limit'
},
questions: {
type: {
description: "Select the type of change that you're committing:",
enum: {
feat: {
description: 'A new feature',
title: 'Features',
emoji: 'β¨',
},
fix: {
description: 'A bug fix',
title: 'Bug Fixes',
emoji: 'π',
}
}
},
scope: {
description: 'What is the scope of this change (e.g. component or file name)'
},
subject: {
description: 'Write a short, imperative tense description of the change'
},
body: {
description: 'Provide a longer description of the change'
},
isBreaking: {
description: 'Are there any breaking changes?',
default: false
},
breakingBody: {
description: 'A BREAKING CHANGE commit requires a body. Please enter a longer description of the commit itself'
},
breaking: {
description: 'Describe the breaking changes'
},
isIssueAffected: {
description: 'Does this change affect any open issues?',
default: false
},
issuesBody: {
description: 'If issues are closed, the commit requires a body. Please enter a longer description of the commit itself'
},
issues: {
description: 'Add issue references (e.g. "fix #123", "re #123".)'
}
}
}
};