CZ Customizable is a Commitizen adapter that enables interactive commit message creation following configurable patterns. It provides customizable prompts for commit types, scopes, and messages, supporting both standalone usage and integration as a Commitizen plugin with extensive configuration options.
npm install cz-customizableESM:
import czCustomizable from "cz-customizable";CommonJS:
const czCustomizable = require("cz-customizable");For standalone usage:
npx cz-customizable
# or
npx cz-custAs a Commitizen plugin:
{
"config": {
"commitizen": {
"path": "cz-customizable"
}
}
}Configuration file (.cz-config.js):
module.exports = {
types: [
{ value: 'feat', name: 'feat: A new feature' },
{ value: 'fix', name: 'fix: A bug fix' },
{ value: 'docs', name: 'docs: Documentation only changes' }
],
scopes: [
{ name: 'auth' },
{ name: 'ui' },
{ name: 'api' }
],
allowCustomScopes: true,
allowBreakingChanges: ['feat', 'fix'],
subjectLimit: 100
};Programmatic usage:
const czCustomizable = require('cz-customizable');
const inquirer = require('inquirer');
czCustomizable.prompter(inquirer, (commitMessage) => {
// Handle the generated commit message
console.log('Generated commit:', commitMessage);
});CZ Customizable is built around several key components:
The core functionality that guides users through interactive commit message creation with configurable prompts.
/**
* Main prompter function that starts the interactive commit process
* @param cz - Commitizen inquirer instance for handling user prompts
* @param commit - Function to execute with the generated commit message
*/
function prompter(cz: any, commit: (message: string) => void): void;Usage Example:
const czCustomizable = require('cz-customizable');
const inquirer = require('inquirer');
// Use with Commitizen
czCustomizable.prompter(inquirer, (commitMessage) => {
execSync(`git commit -m "${commitMessage}"`, { stdio: [0, 1, 2] });
});Loads configuration from various sources with fallback priority.
/**
* Reads configuration from multiple sources in priority order
* @param CZ_CONFIG_NAME - Name of config file to search for (default: '.cz-config.js')
* @returns Configuration object or null if not found
*/
function readConfigFile(CZ_CONFIG_NAME?: string): Config | null;Configuration Sources (in priority order):
.cz-config.js file in project root or .config/cz-config.js.cz-config.json file in project root or .config/cz-config.jsonpackage.json config block: config.cz-customizable.config~/.cz-config.js or ~/.config/cz-config.jsThe function searches these locations and returns the first valid configuration found, or null if no configuration is located.
Dynamically generates inquirer questions based on configuration and runtime context.
/**
* Generates inquirer questions based on configuration
* @param config - Configuration object defining prompts and options
* @param cz - Commitizen instance for creating separators and choices
* @returns Array of inquirer question objects
*/
function getQuestions(config: Config, cz: any): Question[];Constructs final commit messages from user answers and configuration.
/**
* Builds formatted commit message from answers and configuration
* @param answers - User answers from interactive prompts
* @param config - Configuration object with formatting options
* @returns Formatted commit message string
*/
function buildCommit(answers: Answers, config: Config): string;Retrieves prepared commit messages from git for reuse.
/**
* Reads prepared commit message from git
* @param filePath - Path to commit message file (default: './.git/COMMIT_EDITMSG')
* @returns Commit message string or null if not found
*/
function getPreparedCommit(filePath?: string): string | null;interface Config {
/** Required: Array of commit types available for selection */
types: Option[];
/** Optional: Predefined scopes for commits */
scopes?: Option[];
/** Optional: Type-specific scope overrides */
scopeOverrides?: { [type: string]: Option[] };
/** Optional: Custom prompt messages */
messages?: Messages;
/** Optional: Allow custom scope input (default: false) */
allowCustomScopes?: boolean;
/** Optional: Commit types that can have breaking changes */
allowBreakingChanges?: string[];
/** Optional: Questions to skip in the prompt flow */
skipQuestions?: string[];
/** Optional: Subject line character limit (default: 100) */
subjectLimit?: number;
/** Optional: Enable ticket number input */
allowTicketNumber?: boolean;
/** Optional: Ticket number validation regex */
ticketNumberRegExp?: string;
/** Optional: Ticket number prefix */
ticketNumberPrefix?: string;
/** Optional: Ticket number suffix */
ticketNumberSuffix?: string;
/** Optional: Fallback ticket number when none provided */
fallbackTicketNumber?: string;
/** Optional: Require ticket number input */
isTicketNumberRequired?: boolean;
/** Optional: Prepend ticket to commit head instead of after scope */
prependTicketToHead?: boolean;
/** Optional: Custom breaking change prefix (default: 'BREAKING CHANGE:') */
breakingPrefix?: string;
/** Optional: Custom footer prefix (default: 'ISSUES CLOSED:') */
footerPrefix?: string;
/** Optional: Character for line breaks in body/footer (default: '|') */
breaklineChar?: string;
/** Optional: Use prepared commit from .git/COMMIT_EDITMSG */
usePreparedCommit?: boolean;
/** Optional: Uppercase first letter of subject */
upperCaseSubject?: boolean;
/** Optional: Ask breaking change question first */
askForBreakingChangeFirst?: boolean;
/** Optional: Skip empty scopes in prompt */
skipEmptyScopes?: boolean;
/** Optional: Type prefix for commit messages */
typePrefix?: string;
/** Optional: Type suffix for commit messages */
typeSuffix?: string;
/** Optional: Additional custom questions */
additionalQuestions?: AdditionalQuestion[];
/** Optional: Subject separator (default: ': ') */
subjectSeparator?: string;
}
interface Option {
/** Display name for the option */
name: string;
/** Optional value (defaults to name if not provided) */
value?: string;
}
interface Messages {
/** Prompt message for commit type selection */
type?: string;
/** Prompt message for scope selection */
scope?: string;
/** Prompt message for custom scope input */
customScope?: string;
/** Prompt message for subject input */
subject?: string;
/** Prompt message for body input */
body?: string;
/** Prompt message for breaking changes */
breaking?: string;
/** Prompt message for footer input */
footer?: string;
/** Prompt message for commit confirmation */
confirmCommit?: string;
/** Prompt message for ticket number input */
ticketNumber?: string;
/** Alternative ticket number pattern message */
ticketNumberPattern?: string;
}
interface AdditionalQuestion {
/** Question name/identifier */
name: string;
/** Question type (input, list, etc.) */
type: string;
/** Question prompt message */
message: string;
/** Mapping for including answer in commit body */
mapping: string;
}
interface Answers {
/** Selected commit type */
type: string;
/** Selected or custom scope */
scope?: string;
/** Commit subject line */
subject: string;
/** Optional commit body */
body?: string;
/** Optional breaking changes description */
breaking?: string;
/** Optional footer text */
footer?: string;
/** Ticket number if enabled */
ticketNumber?: string;
/** Commit confirmation choice */
confirmCommit: 'yes' | 'no' | 'edit';
/** Additional question answers */
[key: string]: any;
}
interface Question {
/** Question type (list, input, expand, etc.) */
type: string;
/** Question identifier */
name: string;
/** Question prompt message */
message: string;
/** Available choices for list questions */
choices?: any[];
/** Conditional display function */
when?: (answers: Answers) => boolean;
/** Input validation function */
validate?: (value: any) => boolean | string;
/** Input filter/transform function */
filter?: (value: any) => any;
/** Default value or function */
default?: any;
/** Allow re-asking answered questions */
askAnswered?: boolean;
}The package provides two CLI commands:
cz-customizable: Main standalone commandcz-cust: Shortened alias for the main commandBoth commands provide the same interactive commit experience without requiring Commitizen setup. The standalone commands automatically execute git commit with the generated message and handle git command failures gracefully by displaying error information to the user.
Standalone Usage:
# Global installation
npm install -g cz-customizable
cz-customizable
# Or use npx
npx cz-customizable
# Using the short alias
npx cz-custThe package handles several error conditions:
.cz-config.js, .cz-config.json, or package.json config is foundticketNumberRegExp is set, with support for required vs optional ticket numbers.git/COMMIT_EDITMSG files when usePreparedCommit is enabledComplex configuration with all options:
module.exports = {
types: [
{ value: 'feat', name: 'feat: β¨ A new feature' },
{ value: 'fix', name: 'fix: π A bug fix' },
{ value: 'docs', name: 'docs: π Documentation changes' },
{ value: 'style', name: 'style: π Code style changes' },
{ value: 'refactor', name: 'refactor: π¦ Code refactoring' },
{ value: 'perf', name: 'perf: π Performance improvements' },
{ value: 'test', name: 'test: π¨ Adding missing tests' },
{ value: 'chore', name: 'chore: π§ Build process or auxiliary tools' },
{ value: 'revert', name: 'revert: βͺ Revert to a commit' },
{ value: 'WIP', name: 'WIP: π§ Work in progress' }
],
scopes: [
{ name: 'auth' },
{ name: 'ui' },
{ name: 'api' },
{ name: 'database' },
{ name: 'config' }
],
scopeOverrides: {
fix: [
{ name: 'merge' },
{ name: 'style' },
{ name: 'test' }
]
},
messages: {
type: 'Select the type of change that you are committing:',
scope: '\\nDenote the SCOPE of this change (optional):',
customScope: 'Denote the SCOPE of this change:',
subject: 'Write a SHORT, IMPERATIVE tense description of the change:\\n',
body: 'Provide a LONGER description of the change (optional). Use "|" to break new line:\\n',
breaking: 'List any BREAKING CHANGES (optional):\\n',
footer: 'List any ISSUES CLOSED by this change (optional). E.g.: #31, #34:\\n',
confirmCommit: 'Are you sure you want to proceed with the commit above?'
},
allowCustomScopes: true,
allowBreakingChanges: ['feat', 'fix'],
skipQuestions: ['body'],
subjectLimit: 100,
breaklineChar: '|',
footerPrefix: 'ISSUES CLOSED:',
askForBreakingChangeFirst: true,
// Ticket number configuration
allowTicketNumber: true,
isTicketNumberRequired: false,
ticketNumberPrefix: 'TICKET-',
ticketNumberSuffix: ':',
ticketNumberRegExp: '\\d{1,5}',
fallbackTicketNumber: '000',
// Additional custom questions
additionalQuestions: [
{
name: 'reviewers',
type: 'input',
message: 'List any reviewers for this change (optional):',
mapping: 'Reviewers:'
}
],
// Commit message formatting
typePrefix: '[',
typeSuffix: ']',
subjectSeparator: ' - ',
upperCaseSubject: true
};