or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-cz-customizable

Commitizen customizable adapter for creating interactive commit messages following configurable patterns.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/cz-customizable@7.5.x

To install, run

npx @tessl/cli install tessl/npm-cz-customizable@7.5.0

index.mddocs/

CZ Customizable

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.

Package Information

  • Package Name: cz-customizable
  • Package Type: npm
  • Language: JavaScript/Node.js
  • Installation: npm install cz-customizable

Core Imports

ESM:

import czCustomizable from "cz-customizable";

CommonJS:

const czCustomizable = require("cz-customizable");

For standalone usage:

npx cz-customizable
# or
npx cz-cust

Basic Usage

As 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);
});

Architecture

CZ Customizable is built around several key components:

  • Prompter: Main interface that orchestrates the commit message creation process
  • Configuration System: Flexible configuration loading from multiple sources (.cz-config.js, package.json, .cz-config.json)
  • Question Generator: Dynamic question generation based on configuration and user selections
  • Commit Builder: Formats final commit messages according to conventional patterns
  • Standalone CLI: Independent command-line tool for usage without Commitizen setup

Capabilities

Interactive Commit Prompting

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] });
});

Configuration Loading

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):

  1. .cz-config.js file in project root or .config/cz-config.js
  2. .cz-config.json file in project root or .config/cz-config.json
  3. package.json config block: config.cz-customizable.config
  4. Home directory fallback: ~/.cz-config.js or ~/.config/cz-config.js

The function searches these locations and returns the first valid configuration found, or null if no configuration is located.

Question Generation

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[];

Commit Message Building

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;

Previous Commit Reading

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;

Configuration Interface

Config Type

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;
}

Binary Commands

The package provides two CLI commands:

  • cz-customizable: Main standalone command
  • cz-cust: Shortened alias for the main command

Both 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-cust

Error Handling

The package handles several error conditions:

  • Missing Configuration: Displays helpful error message with documentation link when no .cz-config.js, .cz-config.json, or package.json config is found
  • Invalid Ticket Numbers: Validates against configured regex patterns when ticketNumberRegExp is set, with support for required vs optional ticket numbers
  • Subject Length Limits: Enforces character limits (default 100) with clear "Exceed limit" feedback message
  • Git Command Failures: In standalone mode, catches git commit execution errors and displays error details while preserving the generated commit message
  • File System Errors: Handles missing or inaccessible config files gracefully with fallback to null configuration
  • Editor Failures: When using commit editing mode, handles non-zero editor exit codes by displaying the generated commit message
  • Prepared Commit Errors: Safely handles missing or inaccessible .git/COMMIT_EDITMSG files when usePreparedCommit is enabled

Advanced Configuration Examples

Complex 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
};