--- url: /guides/getting-started.md --- # Getting started ## Install Install `@commitlint/cli` and a `@commitlint/config-*` / `commitlint-config-*` of your choice as devDependency and configure `commitlint` to use it. ::: code-group ```sh [npm] npm install -D @commitlint/cli @commitlint/config-conventional ``` ```sh [yarn] yarn add -D @commitlint/cli @commitlint/config-conventional ``` ```sh [pnpm] pnpm add -D @commitlint/cli @commitlint/config-conventional ``` ```sh [bun] bun add -d @commitlint/cli @commitlint/config-conventional ``` ```sh [deno] deno add -D npm:@commitlint/cli npm:@commitlint/config-conventional ``` ::: ## Configuration Configure commitlint to use conventional config ::: code-group ```sh [Linux / macOS] echo "export default { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js ``` ```sh [Windows] # Here we use the node command to avoid encoding issue on Windows. node -e "fs.writeFileSync('commitlint.config.js', process.argv[1])" "export default { extends: ['@commitlint/config-conventional'] };" ``` ::: > \[!WARNING] > Node v24 changes the way that modules are loaded, and this includes the commitlint config file. If your project does not contain a `package.json`, commitlint may fail to load the config, resulting in a `Please add rules to your commitlint.config.js` error message. This can be fixed by doing either of the following: > > * Add a `package.json` file, declaring your project as an ES6 module. This can be done easily by running `npm init es6`. > * Rename the config file from `commitlint.config.js` to `commitlint.config.mjs`. Refer to [configuration documentation](/reference/configuration) for more information. --- --- url: /guides/local-setup.md --- # Guide: Local setup Get high commit message quality and short feedback cycles by linting commit messages right when they are authored. This guide demonstrates how to achieve this via git hooks. Follow the [Getting Started](/guides/getting-started) for basic installation and configuration instructions. ## Add hook To use commitlint you need to setup `commit-msg` hook (currently `pre-commit` hook is not supported) ### Using a git hooks manager To lint commits before they are created you can use [Husky](https://typicode.github.io/husky/)'s `commit-msg` hook. You can find complete setup instructions on the [official documentation](https://typicode.github.io/husky/get-started.html). > \[!NOTE] > The following instructions are meant to `husky@v9` if you are using a different version > consult the official documentation of your version. *** ::::tabs \=== Linux / macOS :::tabs \== npm ```sh npm install --save-dev husky # husky@v9 npx husky init # husky@v8 or lower npx husky install # Add commit message linting to commit-msg hook echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg ``` As an alternative you can create a script inside `package.json` ```sh npm pkg set scripts.commitlint="commitlint --edit" echo "npm run commitlint \${1}" > .husky/commit-msg ``` \== yarn ```sh yarn add --dev husky # husky@v9 yarn husky init # husky@v8 or lower yarn husky install # Add commit message linting to commit-msg hook echo "yarn commitlint --edit \$1" > .husky/commit-msg ``` As an alternative you can create a script inside `package.json` ```sh npm pkg set scripts.commitlint="commitlint --edit" echo "yarn commitlint \${1}" > .husky/commit-msg ``` > \[!WARNING] > Please note that currently @commitlint/cli doesn't support yarn v2 Plug'n'Play (using yarn > v2 with `nodeLinker: node-modules` in your .yarnrc.yml file may work sometimes) \== pnpm ```sh pnpm add --save-dev husky # husky@v9 pnpm husky init # husky@v8 or lower pnpm husky install # Add commit message linting to commit-msg hook echo "pnpm dlx commitlint --edit \$1" > .husky/commit-msg ``` As an alternative you can create a script inside `package.json` ```sh npm pkg set scripts.commitlint="commitlint --edit" echo "pnpm commitlint \${1}" > .husky/commit-msg ``` \== bun ```sh bun add --dev husky # husky@v9 bunx husky init # husky@v8 or lower bunx husky install # Add commit message linting to commit-msg hook echo "bunx commitlint --edit \$1" > .husky/commit-msg ``` \== deno ```sh deno add --dev husky # husky@v9 deno task --eval husky init # husky@v8 or lower deno task --eval husky install # Add commit message linting to commit-msg hook echo "deno task --eval commitlint --edit \$1" > .husky/commit-msg ``` ::: \=== Windows :::tabs \== npm ```sh npm install --save-dev husky # husky@v9 npx husky init # husky@v8 or lower npx husky install # Add commit message linting to commit-msg hook node -e "fs.writeFileSync('.husky/commit-msg', 'npx --no -- commitlint --edit $'+'1\n')" ``` As an alternative you can create a script inside `package.json` ```sh npm pkg set scripts.commitlint="commitlint --edit" node -e "fs.writeFileSync('.husky/commit-msg', 'npm run commitlint $'+'{1}\n')" ``` \== yarn ```sh yarn add --dev husky # husky@v9 yarn husky init # husky@v8 or lower yarn husky install # Add commit message linting to commit-msg hook node -e "fs.writeFileSync('.husky/commit-msg', 'yarn commitlint --edit $'+'1\n')" ``` As an alternative you can create a script inside `package.json` ```sh npm pkg set scripts.commitlint="commitlint --edit" node -e "fs.writeFileSync('.husky/commit-msg', 'yarn commitlint $'+'{1}\n')" ``` > \[!WARNING] > Please note that currently @commitlint/cli doesn't support yarn v2 Plug'n'Play (using yarn > v2 with `nodeLinker: node-modules` in your .yarnrc.yml file may work sometimes) \== pnpm ```sh pnpm add --save-dev husky # husky@v9 pnpm husky init # husky@v8 or lower pnpm husky install # Add commit message linting to commit-msg hook node -e "fs.writeFileSync('.husky/commit-msg', 'pnpm dlx commitlint --edit $'+'1\n')" ``` As an alternative you can create a script inside `package.json` ```sh npm pkg set scripts.commitlint="commitlint --edit" node -e "fs.writeFileSync('.husky/commit-msg', 'pnpm commitlint $'+'{1}\n')" ``` \== bun ```sh bun add --dev husky # husky@v9 bunx husky init # husky@v8 or lower bunx husky install # Add commit message linting to commit-msg hook node -e "fs.writeFileSync('.husky/commit-msg', 'bunx commitlint --edit $'+'1\n')" ``` \== deno ```sh deno add --dev husky # husky@v9 deno task --eval husky init # husky@v8 or lower deno task --eval husky install # Add commit message linting to commit-msg hook node -e "fs.writeFileSync('.husky/commit-msg', 'deno task --eval commitlint --edit $'+'1\n')" ``` ::: :::: *** ### Using git hooks Info about git hooks can be found on [Git documentation](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks). > \[!WARNING] > It's necessary that you use **commit-msg** as the name for hook file. ## Test ### Test simple usage For a first simple usage test of commitlint you can do the following: ::: code-group ```sh [npm] npx commitlint --from HEAD~1 --to HEAD --verbose ``` ```sh [yarn] yarn commitlint --from HEAD~1 --to HEAD --verbose ``` ```sh [pnpm] pnpm commitlint --from HEAD~1 --to HEAD --verbose ``` ```sh [bun] bun commitlint --from HEAD~1 --to HEAD --verbose ``` ```sh [deno] deno task --eval commitlint --from HEAD~1 --to HEAD --verbose ``` ::: This will check your last commit and return an error if invalid or a positive output if valid. ::: tip To try commitlint without creating a configuration file first, pipe a message to it and use the built-in default config ([@commitlint/config-conventional](https://www.npmjs.com/package/@commitlint/config-conventional)): ```sh echo "feat: add new feature" | npx commitlint --default-config ``` ::: ### Test the hook You can test the hook by simply committing. You should see something like this if everything works. ```bash git commit -m "foo: this will fail" # husky > commit-msg No staged files match any of provided globs. ⧗ --- input --- foo: this will fail ✖ type must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test] [type-enum] ✖ found 1 problems, 0 warnings ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint husky - commit-msg script failed (code 1) ``` Since [v8.0.0](https://github.com/conventional-changelog/commitlint/releases/tag/v8.0.0) `commitlint` won't output anything if there are no problems with your commit.\ (You can use the `--verbose` flag to get positive output) ```bash git commit -m "chore: lint on commitmsg" # husky > pre-commit No staged files match any of provided globs. # husky > commit-msg ``` Since [v21.0.0](https://github.com/conventional-changelog/commitlint/releases/tag/v21.0.0) `commitlint` will output the commit message after a new line (EOL) instead of after a colon.\ (You can use the `--legacy-output` flag to get the previous output format used in older versions) Local linting is fine for fast feedback but can easily be tinkered with. To ensure all commits are linted you'll want to check commits on an automated CI Server too. Learn how to in the [CI Setup guide](/guides/ci-setup). --- --- url: /guides/ci-setup.md --- # Guide: CI Setup Enforce commit conventions with confidence by linting on your CI servers with `commitlint`. This guide assumes you have already configured `commitlint` for local usage. Follow the [Getting Started](/guides/getting-started) for basic installation and configuration instructions. ## GitHub Actions An example of how a GitHub Actions workflow could validate the last commit message or all commit messages inside a Pull Request: ```yml name: CI on: [push, pull_request] permissions: contents: read jobs: commitlint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup node uses: actions/setup-node@v4 with: node-version: lts/* cache: npm - name: Install commitlint run: npm install -D @commitlint/cli @commitlint/config-conventional - name: Print versions run: | git --version node --version npm --version npx commitlint --version - name: Validate current commit (last commit) with commitlint if: github.event_name == 'push' run: npx commitlint --last --verbose - name: Validate PR commits with commitlint if: github.event_name == 'pull_request' run: npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose ``` ## Travis ```bash # Install and configure if needed npm install --save-dev @commitlint/travis-cli ``` ```yml # travis.yml language: node_js node_js: - node script: - commitlint-travis ``` ## CircleCI It's just a simple example of how CircleCI configuration file could look like to validate last commit message ```yml version: 2.1 executors: my-executor: docker: - image: cimg/node:current working_directory: ~/project jobs: setup: executor: my-executor steps: - checkout - restore_cache: key: lock-{{ checksum "package-lock.json" }} - run: name: Install dependencies command: npm install - save_cache: key: lock-{{ checksum "package-lock.json" }} paths: - node_modules - persist_to_workspace: root: ~/project paths: - node_modules lint_commit_message: executor: my-executor steps: - checkout - attach_workspace: at: ~/project - run: name: Define environment variable with latest commit's message command: | echo 'export COMMIT_MESSAGE=$(git log -1 --pretty=format:"%s")' >> $BASH_ENV source $BASH_ENV - run: name: Lint commit message command: echo "$COMMIT_MESSAGE" | npx commitlint workflows: version: 2.1 commit: jobs: - setup - lint_commit_message: requires: - setup ``` ## GitLab CI ```yaml lint:commit: image: registry.hub.docker.com/library/node:alpine variables: GIT_DEPTH: 0 before_script: - apk add --no-cache git - npm install --save-dev @commitlint/config-conventional @commitlint/cli script: - npx commitlint --from ${CI_MERGE_REQUEST_DIFF_BASE_SHA} --to ${CI_COMMIT_SHA} ``` GitLab limits `git clone` depth to [20 commits by default](https://docs.gitlab.com/ee/ci/pipelines/settings.html#limit-the-number-of-changes-fetched-during-clone). Setting `GIT_DEPTH: 0` removes this limitation, so `commitlint` can check larger MRs. ## GitLab CI with pre-build container ```yaml stages: ["lint", "build", "test"] lint:commit: image: name: registry.hub.docker.com/commitlint/commitlint:latest entrypoint: [""] stage: lint script: # Uncomment the next line if you are extending the @commitlint/config-nx-scopes in your commitlint configuration #- npm i -g nx@$(node -pe "require('./package.json').devDependencies.nx") - commitlint --from ${CI_MERGE_REQUEST_DIFF_BASE_SHA} --to ${CI_COMMIT_SHA} ``` ## Jenkins X ```yml apiVersion: tekton.dev/v1beta1 kind: PipelineRun metadata: name: pullrequest spec: pipelineSpec: tasks: - name: conventional-commits taskSpec: steps: - name: lint-commit-messages image: commitlint/commitlint script: | #!/usr/bin/env sh . .jx/variables.sh commitlint --extends '@commitlint/config-conventional' --from $PR_BASE_SHA --to $PR_HEAD_SHA serviceAccountName: tekton-bot timeout: 15m ``` ## BitBucket Validate commits within a PR by leveraging [BitBucket\`s default variables](https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/): ```yml image: node:22 pipelines: pull-requests: default: - step: name: Lint commit messages script: - npm install --save-dev @commitlint/config-conventional @commitlint/cli - npx commitlint --from $BITBUCKET_COMMIT~$(git rev-list --count $BITBUCKET_BRANCH ^origin/$BITBUCKET_PR_DESTINATION_BRANCH) --to $BITBUCKET_COMMIT --verbose ``` BitBucket limits git clone depth to 20 commits by default. You can change this behaviour by [changing the `clone` option](https://support.atlassian.com/bitbucket-cloud/docs/git-clone-behavior/). ## Azure Pipelines ```yml steps: - checkout: self fetchDepth: 0 - task: NodeTool@0 inputs: versionSpec: "20.x" checkLatest: true - script: | git --version node --version npm --version npx commitlint --version displayName: Print versions - script: | npm install conventional-changelog-conventionalcommits npm install commitlint@latest displayName: Install commitlint - script: npx commitlint --last --verbose condition: ne(variables['Build.Reason'], 'PullRequest') displayName: Validate current commit (last commit) with commitlint - script: | echo "Accessing Azure DevOps API..." response=$(curl -s -X GET -H "Cache-Control: no-cache" -H "Authorization: Bearer $(System.AccessToken)" $(System.TeamFoundationCollectionUri)$(System.TeamProject)/_apis/git/repositories/$(Build.Repository.Name)/pullRequests/$(System.PullRequest.PullRequestId)/commits?api-version=6.0) numberOfCommits=$(echo "$response" | jq -r '.count') echo "$numberOfCommits commits to check" npx commitlint --from $(System.PullRequest.SourceCommitId)~${numberOfCommits} --to $(System.PullRequest.SourceCommitId) --verbose condition: eq(variables['Build.Reason'], 'PullRequest') displayName: Validate PR commits with commitlint ``` ### 3rd party integrations #### [Codemagic](https://codemagic.io/) ```yaml #codemagic.yaml workflows: commitlint: name: Lint commit message scripts: - npx commitlint --from=HEAD~1 ``` > \[!TIP] > Help yourself adopting a commit convention by using an interactive commit prompt. > Learn how to use `@commitlint/prompt-cli` in the [Use prompt guide](/guides/use-prompt). --- --- url: /guides/use-prompt.md --- # Guide: Use prompt > \[!WARNING] > Prompt is currently unmaintained\ > Some things might not work as expected `@commitlint/prompt-cli` helps with fast authoring of commit messages and ensures they adhere to the commit convention configured in `commitlint.config.js`. ## Install 1. Create a git repository if needed ```sh git init ``` 2. Create a package.json if needed ::: code-group ```sh [npm] npm init ``` ```sh [yarn] yarn init ``` ```sh [pnpm] pnpm init ``` ```sh [bun] bun init ``` ::: 3. Install and configure if needed ::: code-group ```sh [npm] npm install --save-dev @commitlint/cli @commitlint/config-conventional @commitlint/prompt-cli echo "export default { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js ``` ```sh [yarn] yarn add --dev @commitlint/cli @commitlint/config-conventional @commitlint/prompt-cli echo "export default { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js ``` ```sh [pnpm] pnpm add --save-dev @commitlint/cli @commitlint/config-conventional @commitlint/prompt-cli echo "export default { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js ``` ```sh [bun] bun add --dev @commitlint/cli @commitlint/config-conventional @commitlint/prompt-cli echo "export default { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js ``` ```sh [deno] deno add --dev npm:@commitlint/cli npm:@commitlint/config-conventional npm:@commitlint/prompt-cli echo "export default { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js ``` ::: ## Provide a shortcut To make prompt-cli easy to use, add a npm run-script to your `package.json` ```json:line-numbers {3} { "scripts": { "commit": "commit" } } ``` Test the prompt by executing ::: code-group ```sh [npm] git add . npm run commit ``` ```sh [yarn] git add . yarn commit ``` ```sh [pnpm] git add . pnpm commit ``` ```sh [bun] git add . bun commit ``` ```sh [deno] git add . deno task commit ``` ::: # An alternative to `@commitlint/prompt-cli`: commitizen Another way to author commit messages that adhere to the commit convention configured in `commitlint.config.js` is to use `commitizen`. For more information, checkout their [official website](http://commitizen.github.io/cz-cli/). commitlint provides two adapters for `commitizen`: 1. `@commitlint/prompt` provides a way to interact same as `@commitlint/prompt-cli` 2. `@commitlint/cz-commitlint` is inspired by [cz-conventional-changelog](https://github.com/commitizen/cz-conventional-changelog), it provides a more modern way to interact. --- --- url: /guides/ai-agents.md --- # Guide: AI Agents AI coding agents (Claude Code, Copilot, Cursor, and others) write a growing share of commits. Your commitlint configuration is a machine-readable contract: agents follow rules that are enforced and drift on rules that are not. This guide shows how to make agents first-class citizens of your commit convention. ## Install the commitlint skill For tools that support the [Agent Skills](https://agentskills.io) format (such as Claude Code), commitlint ships a skill that teaches the agent to read your resolved configuration, write a compliant message, and self-correct when the commit-msg hook rejects: ```bash mkdir -p .claude/skills/committing-with-commitlint curl -fLo .claude/skills/committing-with-commitlint/SKILL.md \ https://raw.githubusercontent.com/conventional-changelog/commitlint/master/skills/committing-with-commitlint/SKILL.md ``` For other tools, consult their documentation for where skills are loaded from — the skill file itself is tool-neutral. ## No skill support? Add this to your `AGENTS.md`, `CLAUDE.md`, or equivalent agent instructions file: ```markdown ## Commit convention This repository enforces its commit convention with commitlint. - Read the rules before committing: `npx commitlint --print-config json` - Validate a message before using it: `printf '%s' "" | npx commitlint` (exit 0 = valid) - If the commit-msg hook rejects a commit, fix the rules named in brackets (e.g. `[subject-case]`) and retry. Never use `git commit --no-verify`. ``` ## CLI primitives for agents and automation | Command | Purpose | | --------------------------------------- | -------------------------------------------------------------- | | `npx commitlint --print-config json` | The resolved configuration (rules, parser preset) as JSON | | `printf '%s' "" \| npx commitlint` | Validate a candidate message from stdin | | `npx commitlint --last` | Lint the last commit | | `npx commitlint --edit $1` | Lint a commit-msg file (hook usage) | | `npx commitlint --strict` | Exit code 2 for warnings, 3 for errors (instead of 1 for both) | ## For LLMs reading these docs The full documentation is available in LLM-friendly form: * — index of all pages * — entire documentation as one markdown file --- --- url: /reference/cli.md --- # CLI ```sh-vue ❯ npx commitlint --help @commitlint/cli@{{ commitlintVersion }} - Lint your commit messages [input] reads from stdin if --edit, --env, --from and --to are omitted Options: -c, --color toggle colored output [boolean] [default: true] -g, --config path to the config file; result code 9 if config is missing [string] --default-config use built-in default config (@commitlint/config-conventional) when no rules are found [boolean] --print-config print resolved config [string] [choices: "", "text", "json"] -d, --cwd directory to execute in [string] [default: (Working Directory)] -e, --edit read last commit message from the specified file or fallbacks to ./.git/COMMIT_EDITMSG [string] -E, --env check message in the file at path given by environment variable value [string] -x, --extends array of shareable configurations to extend [array] -H, --help-url help url in error message [string] -f, --from lower end of the commit range to lint; applies if edit=false [string] --from-last-tag uses the last tag as the lower end of the commit range to lint; applies if edit=false and from is not set [boolean] --git-log-args additional git log arguments as space separated string, example '--first-parent --cherry-pick' [string] -l, --last just analyze the last commit; applies if edit=false [boolean] -o, --format output format of the results [string] -p, --parser-preset configuration preset to use for conventional-commits-parser [string] -q, --quiet toggle console output [boolean] [default: false] -t, --to upper end of the commit range to lint; applies if edit=false [string] -V, --verbose enable verbose output for reports without problems [boolean] --legacy-output use the legacy input output format (single-line 'input: ...') [boolean] -s, --strict enable strict mode; result code 2 for warnings, 3 for errors [boolean] --options path to a JSON file or Common.js module containing CLI options -v, --version display version information [boolean] -h, --help Show help [boolean] ``` ## Lint without a config file By default commitlint requires a configuration with rules to run — without one it exits with an error (result code 9). Pass `--default-config` to fall back to the built-in default config ([@commitlint/config-conventional](https://www.npmjs.com/package/@commitlint/config-conventional)) when no rules are found, so commitlint can run without any setup: ```sh echo "feat: add new feature" | npx commitlint --default-config ``` This is useful for one-off checks or server-side hooks (e.g. `pre-receive`) where creating a `commitlint.config.js` and installing a shareable config is not practical. A configuration file with rules always takes precedence over `--default-config`, and configs passed via `--extends` are kept and override the default config when the fallback applies. --- --- url: /reference/configuration.md --- # Configuration ## Config via file `@commitlint/cli` picks up configuration from the following files: * `.commitlintrc` * `.commitlintrc.json` * `.commitlintrc.yaml` * `.commitlintrc.yml` * `.commitlintrc.js` * `.commitlintrc.cjs` * `.commitlintrc.mjs` * `.commitlintrc.ts` * `.commitlintrc.cts` * `.commitlintrc.mts` * `commitlint.config.js` * `commitlint.config.cjs` * `commitlint.config.mjs` * `commitlint.config.ts` * `commitlint.config.cts` * `commitlint.config.mts` The file is expected * to contain valid JavaScript / TypeScript * export a configuration object * adhere to the schema outlined below Configuration files are resolved using [cosmiconfig](https://github.com/cosmiconfig/cosmiconfig). ## Config via `package.json` You can add a `commitlint` field in `package.json` (or [`package.yaml`](https://github.com/pnpm/pnpm/pull/1799)) with an object that follows the below structure. ## Config option CLI Add the path to the configuration file. Example: `commitlint --config commitlint.config.js` ## Configuration object example ```js const Configuration = { /* * Resolve and load @commitlint/config-conventional from node_modules. * Referenced packages must be installed */ extends: ["@commitlint/config-conventional"], /* * Resolve and load conventional-changelog-atom from node_modules. * Referenced packages must be installed */ parserPreset: "conventional-changelog-atom", /* * Resolve and load @commitlint/format from node_modules. * Referenced package must be installed */ formatter: "@commitlint/format", /* * Any rules defined here will override rules from @commitlint/config-conventional */ rules: { "type-enum": [2, "always", ["foo"]], }, /* * Array of functions that return true if commitlint should ignore the given message. * Given array is merged with predefined functions, which consist of matchers like: * * - 'Merge pull request', 'Merge X into Y' or 'Merge branch X' * - 'Revert X' * - 'v1.2.3' (ie semver matcher) * - 'Automatic merge X' or 'Auto-merged X into Y' * * To see full list, check https://github.com/conventional-changelog/commitlint/blob/master/%40commitlint/is-ignored/src/defaults.ts. * To disable those ignores and run rules always, set `defaultIgnores: false` as shown below. */ ignores: [(commit) => commit === ""], /* * Whether commitlint uses the default ignore rules, see the description above. */ defaultIgnores: true, /* * Custom URL to show upon failure */ helpUrl: "https://github.com/conventional-changelog/commitlint/#what-is-commitlint", /* * Custom prompt configs */ prompt: { messages: {}, questions: { type: { description: "please input type:", }, }, }, }; export default Configuration; ``` > \[!NOTE] > CJS format is supported as well: > > ```js > module.exports = Configuration; > ``` ### TypeScript configuration Configuration can also be a TypeScript file. Relevant types and enums can be imported from `@commitlint/types`. Below you can see main changes from a standard js file: ```ts import type { UserConfig } from "@commitlint/types"; // [!code focus] import { RuleConfigSeverity } from "@commitlint/types"; // [!code focus] const Configuration: UserConfig = { // [!code focus] extends: ["@commitlint/config-conventional"], parserPreset: "conventional-changelog-atom", formatter: "@commitlint/format", rules: { "type-enum": [RuleConfigSeverity.Error, "always", ["foo"]], // [!code focus] }, // ... }; export default Configuration; ``` ## Shareable configuration Every commitlint configuration can extend other commitlint configurations. Specify configurations to extend via the `.extends` key, using ids that can be resolved by the node resolve algorithm. This means installed npm packages and local files can be used. :::tabs \== npm ```sh npm install --save-dev commitlint-config-lerna @commitlint/config-conventional ``` ::: code-group ```js [commitlint.config.js] export default { extends: [ 'lerna' // prefixed with commitlint-config-*, '@commitlint/config-conventional' // scoped packages are not prefixed ] } ``` \== local ::: code-group ```js [commitlint.config.js] export default { extends: ["./commitlint.base.js", "./commitlint.types.js"], }; ``` ```js [commitlint.types.js] // will be picked up by commitlint.config.js export default { rules: { "type-enum": [2, "always", ["foo"]], }, }; ``` ```js [commitlint.base.js] // will be picked up by commitlint.config.js export default { extends: ["@commitlint/config-conventional"], // extends can be nested parserPreset: "conventional-changelog-atom", }; ``` ::: More information can be found in the [Concepts – shareable config section](/concepts/shareable-config). ## Parser presets The parser preset controls how commit messages are parsed into their component parts (type, scope, subject, body, footer, etc.). By default, commitlint does not ship with a parser preset — it falls back to [`conventional-changelog-angular`](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular) defaults. When you extend `@commitlint/config-conventional`, the [`conventional-changelog-conventionalcommits`](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-conventionalcommits) preset is applied, which follows the [Conventional Commits specification](https://www.conventionalcommits.org/). You can override the parser preset using the `parserPreset` property. It accepts: * A **string** referencing an npm package or local file (resolved via Node's module resolution) * An **object** with a `parserOpts` property for inline configuration ### Using an npm package ```sh npm install --save-dev conventional-changelog-atom ``` ::: code-group ```js [commitlint.config.js] export default { parserPreset: "conventional-changelog-atom", }; ``` ::: ### Using a local file ::: code-group ```js [commitlint.config.js] export default { parserPreset: "./parser-preset", }; ``` ```js [parser-preset.js] export default { parserOpts: { headerPattern: /^(\w*)\((\w*)\)-(\w*)\s(.*)$/, headerCorrespondence: ["type", "scope", "ticket", "subject"], }, }; ``` ::: ### Inline `parserOpts` You can also pass `parserOpts` directly without a separate file. This is useful for small adjustments like custom issue prefixes: ::: code-group ```js [commitlint.config.js] export default { parserPreset: { parserOpts: { issuePrefixes: ["PROJ-", "JIRA-"], }, }, }; ``` ::: ### Common `parserOpts` The parser is powered by [`conventional-commits-parser`](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-commits-parser). Common options include: | Option | Description | | ----------------------- | -------------------------------------------------------------------- | | `headerPattern` | Regex to match the commit header (type, scope, subject) | | `headerCorrespondence` | Array of field names matching the capture groups in headerPattern | | `issuePrefixes` | Prefixes to match issue references (e.g. `["#", "PROJ-"]`) | | `noteKeywords` | Keywords that mark footer notes (e.g. `["BREAKING CHANGE"]`) | | `breakingHeaderPattern` | Regex to detect breaking changes in the header (e.g. the `!` marker) | For the complete list of options, see the [`conventional-commits-parser` documentation](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-commits-parser#options). ### `presetConfig` When using a preset like `conventional-changelog-conventionalcommits`, you can pass a `presetConfig` object to customize the preset's behavior without replacing the entire parser configuration. This is commonly used to set commit types that appear in generated changelogs: ::: code-group ```js [commitlint.config.js] export default { parserPreset: { name: "conventional-changelog-conventionalcommits", presetConfig: { types: [ { type: "feat", section: "Features" }, { type: "fix", section: "Bug Fixes" }, { type: "docs", section: "Documentation", hidden: false }, { type: "chore", hidden: true }, ], }, }, }; ``` ::: The available `presetConfig` options depend on the preset you are using. See the [`conventional-changelog-conventionalcommits` documentation](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-conventionalcommits#preset-configuration) for details. ### Usage with semantic-release If you use [semantic-release](https://github.com/semantic-release/semantic-release), both commitlint and semantic-release can share the same `conventional-changelog-conventionalcommits` preset. Keeping `parserOpts` and `presetConfig` consistent across both tools ensures that commits parsed during linting match what semantic-release uses for versioning and changelog generation: ::: code-group ```js [commitlint.config.js] export default { extends: ["@commitlint/config-conventional"], parserPreset: { name: "conventional-changelog-conventionalcommits", presetConfig: { types: [ { type: "feat", section: "Features" }, { type: "fix", section: "Bug Fixes" }, { type: "docs", section: "Documentation", hidden: false }, ], }, }, }; ``` ::: ```js [.releaserc.js] export default { plugins: [ [ "@semantic-release/commit-analyzer", { preset: "conventionalcommits", presetConfig: { types: [ { type: "feat", section: "Features" }, { type: "fix", section: "Bug Fixes" }, { type: "docs", section: "Documentation", hidden: false }, ], }, }, ], [ "@semantic-release/release-notes-generator", { preset: "conventionalcommits", presetConfig: { types: [ { type: "feat", section: "Features" }, { type: "fix", section: "Bug Fixes" }, { type: "docs", section: "Documentation", hidden: false }, ], }, }, ], ], }; ``` ## Formatter Commitlint can output the issues encountered in different formats, if necessary. Use ids resolvable by the node resolve algorithm. ```js export default { formatter: "@commitlint/format", }; ``` ## Rules Refer to [Rules](/reference/rules.md) for a complete list of available rules. ## Prompt Config command-line submit interaction, works with `@commitlint/cz-commitlint`. Refer to [Prompt Config](/reference/prompt.md) for details. --- --- url: /reference/rules-configuration.md --- # Rules configuration Rules are made up by a name and a configuration array. The configuration array contains: * **Level** `[0..2]`: `0` disables the rule. For `1` it will be considered a warning for `2` an error. * **Applicable** `always|never`: `never` inverts the rule. * **Value**: value to use for this rule. Rule configurations are either of type `array` residing on a key with the rule's name as key on the rules `object` or of type function returning type `array` or `Promise`. This means all of the following notations are supported. ## Plain array :::code-group ```js [commitlint.config.js] export default { // ... rules: { "header-max-length": [0, "always", 72], // [!code focus] }, // ... }; ``` ::: ## Function returning array :::code-group ```js [commitlint.config.js] export default { // ... rules: { "header-max-length": () => [0, "always", 72], // [!code focus] }, // ... }; ``` ::: ## Async function returning array :::code-group ```js [commitlint.config.js] export default { // ... rules: { "header-max-length": async () => [0, "always", 72], // [!code focus] }, // ... }; ``` ::: --- --- url: /reference/rules.md --- # Rules ## body-case * **condition**: `body` is in case `value` * **rule**: `always` * **value** ```text 'lower-case' ``` * **possible values** ```js [ "lower-case", // default "upper-case", // UPPERCASE "camel-case", // camelCase "kebab-case", // kebab-case "pascal-case", // PascalCase "sentence-case", // Sentence case "snake-case", // snake_case "start-case", // Start Case ]; ``` ## body-empty * **condition**: `body` is empty * **rule**: `never` ## body-full-stop * **condition**: `body` ends with `value` * **rule**: `never` * **value** ```text '.' ``` ## body-leading-blank * **condition**: `body` begins with blank line * **rule**: `always` ## body-max-length * **condition**: `body` has `value` or less characters * **rule**: `always` * **value** ```text Infinity ``` ## body-max-line-length * **condition**: `body` lines have `value` or less characters, or contain a URL * **rule**: `always` * **value** ```text Infinity ``` ## body-min-length * **condition**: `body` has `value` or more characters * **rule**: `always` * **value** ```text 0 ``` ## breaking-change-exclamation-mark * **condition**: Either both or neither `header` has an exclamation mark before the `:` marker and a line in `footer` matches the regular expression `^BREAKING[ -]CHANGE:` * **rule**: `always` > \[!NOTE] > > This rule enforces that breaking changes are marked by both a `!` in the header > and `BREAKING CHANGE` in the footer. The behavior is that of an XNOR operation: > > * It passes when either both are present or both are not. > * It fails when one is present and the other is not. ## footer-empty * **condition**: `footer` is empty * **rule**: `never` ## footer-leading-blank * **condition**: `footer` begins with blank line * **rule**: `always` ## footer-max-length * **condition**: `footer` has `value` or less characters * **rule**: `always` * **value** ```text Infinity ``` ## footer-max-line-length * **condition**: `footer` lines have `value` or less characters * **rule**: `always` * **value** ```text Infinity ``` ## footer-min-length * **condition**: `footer` has `value` or more characters * **rule**: `always` * **value** ```text 0 ``` ## header-case * **condition**: `header` is in case `value` * **rule**: `always` * **value** ```text 'lower-case' ``` * **possible values** ```js [ "lower-case", // default "upper-case", // UPPERCASE "camel-case", // camelCase "kebab-case", // kebab-case "pascal-case", // PascalCase "sentence-case", // Sentence case "snake-case", // snake_case "start-case", // Start Case ]; ``` ## header-full-stop * **condition**: `header` ends with `value` * **rule**: `never` * **value** ```text '.' ``` ## header-max-length * **condition**: `header` has `value` or less characters * **rule**: `always` * **value** ```text 72 ``` ## header-min-length * **condition**: `header` has `value` or more characters * **rule**: `always` * **value** ```text 0 ``` ## header-trim * **condition**: `header` must not have initial or trailing whitespaces * **rule**: `always` ## references-empty * **condition**: `references` has at least one entry * **rule**: `never` ## scope-case * **condition**: `scope` is in case `value` * **rule**: `always` * **value** ```text 'lower-case' ``` * **possible values** ```js [ "lower-case", // default "upper-case", // UPPERCASE "camel-case", // camelCase "kebab-case", // kebab-case "pascal-case", // PascalCase "sentence-case", // Sentence case "snake-case", // snake_case "start-case", // Start Case ]; ``` * extended value (object based) ```js { cases: ["kebab-case"], delimiters: ["/"] } ``` * `cases` — list of allowed case formats * `delimiters` — optional list of delimiter strings used to split multi-segment scopes (default: `["/", "\", ","]`) ## scope-delimiter-style * **condition**: all delimiters found in `scope` must match `value` * **rule**: `always` * **value** ```text ["/", "\", ","] ``` > \[!NOTE] > > * When using this rule together with [scope-enum](#scope-enum) or [scope-case](#scope-case), make sure to provide the same `delimiters` configuration in those rules as well. Otherwise scope parsing may become inconsistent. ## scope-empty * **condition**: `scope` is empty * **rule**: `never` ## scope-enum * **condition**: `scope` is found in value * **rule**: `always` * **value** ```text [] ``` * extended value (object based) ```js { scopes: ["foo", "bar"], delimiters: ["/"] } ``` * `scopes` — list of allowed scope values * `delimiters` — optional list of delimiter strings used to split multi-segment scopes (default: `["/", "\", ","]`) > \[!NOTE] > > * This rule always passes if no scopes are provided in the message or the value > is an empty array. > * When set to `always`, all message scopes must be found in the value. > * When set to `never`, none of the message scopes can be found in the value. ## scope-max-length * **condition**: `scope` has `value` or less characters * **rule**: `always` * **value** ```text Infinity ``` ## scope-min-length * **condition**: `scope` has `value` or more characters * **rule**: `always` * **value** ```text 0 ``` ## signed-off-by * **condition**: `message` has `value` * **rule**: `always` * **value** ```text 'Signed-off-by:' ``` ## subject-case * **condition**: `subject` is in case `value` * **rule**: `never` * **value** ```js ["sentence-case", "start-case", "pascal-case", "upper-case"]; ``` * **possible values** ```js [ "lower-case", // lower case "upper-case", // UPPERCASE "camel-case", // camelCase "kebab-case", // kebab-case "pascal-case", // PascalCase "sentence-case", // Sentence case "snake-case", // snake_case "start-case", // Start Case ]; ``` ## subject-empty * **condition**: `subject` is empty * **rule**: `never` ## subject-exclamation-mark * **condition**: `subject` has exclamation before the `:` marker * **rule**: `never` ## subject-full-stop * **condition**: `subject` ends with `value` * **rule**: `never` * **value** ```text '.' ``` ## subject-max-length * **condition**: `subject` has `value` or less characters * **rule**: `always` * **value** ```text Infinity ``` ## subject-min-length * **condition**: `subject` has `value` or more characters * **rule**: `always` * **value** ```text 0 ``` ## trailer-exists * **condition**: `message` has trailer `value` * **rule**: `always` * **value** ```text 'Signed-off-by:' ``` ## type-case * **description**: `type` is in case `value` * **rule**: `always` * **value** ```text 'lower-case' ``` * **possible values** ```js [ "lower-case", // default "upper-case", // UPPERCASE "camel-case", // camelCase "kebab-case", // kebab-case "pascal-case", // PascalCase "sentence-case", // Sentence case "snake-case", // snake_case "start-case", // Start Case ]; ``` ## type-empty * **condition**: `type` is empty * **rule**: `never` ## type-enum * **condition**: `type` is found in value * **rule**: `always` * **value** ```js ["build", "chore", "ci", "docs", "feat", "fix", "perf", "refactor", "revert", "style", "test"]; ``` ## type-max-length * **condition**: `type` has `value` or less characters * **rule**: `always` * **value** ```text Infinity ``` ## type-min-length * **condition**: `type` has `value` or more characters * **rule**: `always` * **value** ```text 0 ``` --- --- url: /reference/plugins.md --- # Working with Plugins Our plugin implementation is based off of [eslint's plugin implementation](https://github.com/eslint/eslint/blob/5018378131fd5190bbccca902c0cf4276ee1581a/lib/config/plugins.js); Each plugin is an npm module with a name in the format of `commitlint-plugin-`, such as `commitlint-plugin-jquery`. You can also use scoped packages in the format of `@/commitlint-plugin-` such as `@jquery/commitlint-plugin-jquery`. ## Rules in Plugins Plugins can expose additional rules for use in commitlint. To do so, the plugin must export a `rules` object containing a key-value mapping of rule ID to rule. The rule ID does not have to follow any naming convention (so it can just be `dollar-sign`, for instance). ```js export default { rules: { "dollar-sign": function (parsed, when, value) { // rule implementation ... }, }, }; ``` To use the rule in commitlint, you would use the unprefixed plugin name, followed by a slash, followed by the rule name. So if this plugin were named `commitlint-plugin-myplugin`, then in your configuration you'd refer to the rule by the name `myplugin/dollar-sign`. Example: `"rules": {"myplugin/dollar-sign": 2}`. ## Peer Dependency To make clear that the plugin requires commitlint to work correctly you have to declare commitlint as a `peerDependency` in your `package.json`. The plugin support was introduced in commitlint version `7.6.0`. Ensure the `peerDependency` points to @commitlint `7.6.0` or later. ```json { "peerDependencies": { "@commitlint/lint": ">=7.6.0" } } ``` ## Share Plugins In order to make your plugin available to the community you have to publish it on npm. Recommended keywords: * `commitlint` * `commitlintplugin` Add these keywords into your `package.json` file to make it easy for others to find. ## Local Plugins In case you want to develop your plugins locally without the need to publish to `npm`, you can send declare your plugins inside your project locally. Please be aware that you can declare **only one** local plugin. ### Usage Example ::: code-group ```js [commitlint.config.js] export default { rules: { "hello-world-rule": [2, "always"], }, plugins: [ { rules: { "hello-world-rule": ({ subject }) => { const HELLO_WORLD = "Hello World"; return [ subject.includes(HELLO_WORLD), `Your subject should contain ${HELLO_WORLD} message`, ]; }, }, }, ], }; ``` ::: ```bash > echo "feat: random subject" | commitlint # fails > echo "feat: Hello World" | commitlint # passes ``` ## Further Reading * [npm Developer Guide](https://docs.npmjs.com/misc/developers) --- --- url: /reference/prompt.md --- # Prompt Prompt Config is used by `@commitlint/cz-commitlint`. There are three fields: `settings`, `messages` and `questions` ## `settings` Set optional options. * `enableMultipleScopes`: `(boolean)` Enable multiple scopes, select scope with a radio list, disabled by default. * `scopeEnumSeparator`: `(string)` Commitlint supports [multiple scopes](/concepts/commit-conventions#multiple-scopes), you can specify the delimiter. It is applied when `enableMultipleScopes` set true. * `useExclamationMark`: `(boolean)` Append `!` after the type/scope in the commit header when a breaking change is indicated, disabled by default. ## `messages` Set hint contents, you can configure it to support localization. * `skip`: The field can be skipped by pressing enter * `max`: Maximum number of characters * `min`: Minimum number of characters * `emptyWarning`: The field cannot be empty * `upperLimitWarning`: The character limit is exceeded * `lowerLimitWarning`: The characters are less than lower limit ## `questions` Specify the interactive steps, Steps can only be configured in * `header` * `type` * `scope` * `subject` * `body` * `footer` * `isBreaking` * `breaking` * `breakingBody` * `isIssueAffected` * `issues` * `issuesBody` ![cz-commitlint questions](/assets/cz-commitlint.png) ```js export default { parserPreset: 'conventional-changelog-conventionalcommits', rules: { ... }, prompt: { settings: {}, 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: '🐛', }, docs: { description: 'Documentation only changes', title: 'Documentation', emoji: '📚', }, style: { description: 'Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)', title: 'Styles', emoji: '💎', }, refactor: { description: 'A code change that neither fixes a bug nor adds a feature', title: 'Code Refactoring', emoji: '📦', }, perf: { description: 'A code change that improves performance', title: 'Performance Improvements', emoji: '🚀', }, test: { description: 'Adding missing tests or correcting existing tests', title: 'Tests', emoji: '🚨', }, build: { description: 'Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)', title: 'Builds', emoji: '🛠', }, ci: { description: 'Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)', title: 'Continuous Integrations', emoji: '⚙️', }, chore: { description: "Other changes that don't modify src or test files", title: 'Chores', emoji: '♻️', }, revert: { description: 'Reverts a previous commit', title: 'Reverts', 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?', }, 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?', }, 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".)', }, }, } }; ``` --- --- url: /reference/examples.md --- # Examples These examples show common usages of how commitlint can be configured. ## Validate for issue/ticket numbers This example uses inline `parserOpts` to configure the parser to recognize custom issue prefixes (e.g. `PROJ-123`). The `references-empty` rule then enforces that every commit references a ticket. ::: code-group ```jsonc [package.json] { // ... "commitlint": { "rules": { "references-empty": [2, "never"], }, "parserPreset": { "parserOpts": { "issuePrefixes": ["PROJ-"], }, }, }, // ... } ``` ::: See [Parser presets](/reference/configuration#parser-presets) for common `parserOpts` and configuration examples. ## Customizing Emojis and Alignment in VS Code Some terminals have trouble correctly calculating the width of Unicode emojis, which can cause a missing space after the emoji, leading to misaligned text in the commit prompt. ![cz-commitlint questions](/assets/vs-code-emoji.png) To fix this issue in VS Code, you can specify an additional space after each emoji in your `commitlint.config.ts` file. ::: code-group ```ts [commitlint.config.ts] import { type UserConfig } from "@commitlint/types"; export default { // Use the conventional commit rules as a base. extends: ["@commitlint/config-conventional"], prompt: { questions: { type: { enum: { // Add a space to a few common types for better alignment. build: { emoji: "🛠️ ", // The extra space fixes the alignment. }, chore: { emoji: "♻️ ", }, ci: { emoji: "⚙️ ", }, revert: { emoji: "🗑️ ", }, }, }, }, }, } satisfies UserConfig; ``` ::: ## Include Emojis in Commit Messages By default, emojis are only shown in the commit message prompt. To include them in the actual commit header, you need a custom parser and a setting to enable them. This configuration is based on the conventional commit rules and uses a *parser preset* to validate commit headers that start with an emoji. ::: code-group ```ts [commitlint.config.ts] import type { ParserPreset, UserConfig } from "@commitlint/types"; import config from "@commitlint/config-conventional"; import createPreset from "conventional-changelog-conventionalcommits"; import { merge } from "lodash-es"; // A helper function to create the custom emoji parser preset. async function createEmojiParser(): Promise { // Generates the regex from the emojis defined in the conventional config. const emojiRegexPart = Object.values(config.prompt.questions.type.enum) .map((value) => value.emoji.trim()) .join("|"); const parserOpts = { // This regular expression validates commit headers with an emoji. breakingHeaderPattern: new RegExp(`^(?:${emojiRegexPart})\\s+(\\w*)(?:\\((.*)\\))?!:\\s+(.*)$`), headerPattern: new RegExp(`^(?:${emojiRegexPart})\\s+(\\w*)(?:\\((.*)\\))?!?:\\s+(.*)$`), }; const emojiParser = merge({}, await createPreset(), { conventionalChangelog: { parserOpts }, parserOpts, recommendedBumpOpts: { parserOpts }, }); return emojiParser; } const emojiParser = await createEmojiParser(); export default { extends: ["@commitlint/config-conventional"], parserPreset: emojiParser, prompt: { questions: { type: { enum: { // Customize emojis and add the extra space for better alignment. build: { emoji: "🛠️ " }, chore: { emoji: "♻️ " }, ci: { emoji: "⚙️ " }, revert: { emoji: "🗑️ " }, }, // This setting includes the emoji in the final commit header. headerWithEmoji: true, }, }, }, } satisfies UserConfig; ``` ::: Although some emojis may appear without a trailing space in the terminal, the commit message itself is submitted with the correct formatting. ![cz-commitlint questions](/assets/vs-code-commit-msg.png) You can verify this with `git log -4 --format=%B > commits.txt`. :::code-group ```text [commits.txt] ⚙️ ci(scope): short 🛠 build(scope): short 🐛 fix(scope): short ✨ feat(scope): short ``` ::: --- --- url: /reference/community-projects.md --- # Projects from the community ## Remark ?> These projects are not affiliated with us in any way. Before you use any of these projects make sure you know what you are doing. If you find anything weird about a project let us know. If you want to add a project to this list [open a pull request](https://github.com/conventional-changelog/commitlint/pulls). ## List of Projects * [Gitmoji Commit Workflow](https://github.com/arvinxx/gitmoji-commit-workflow) * [commitlint.io](https://github.com/tomasen/commitlintio) - helps your project to ensure nice and tidy commit messages without needing any download or installation * [commitlint plugin function rules](https://github.com/vidavidorra/commitlint-plugin-function-rules) - use functions as rule value to create rules based on commit messages, with regular expressions and more * [commitlint-plugin-selective-scope](https://github.com/ridvanaltun/commitlint-plugin-selective-scope) - limit scopes per type with regular expressions and plain text * [commitlint-gitlab-ci](https://gitlab.com/dmoonfire/commitlint-gitlab-ci/) - a small wrapper around `commitlint` for working with the quirks of Gitlab's CI without failing jobs * [committier](https://github.com/iamyoki/committier) - fix and format commit messages --- --- url: /api/load.md --- # @commitlint/load Load shared commitlint configuration ## Install ```sh npm install --save @commitlint/load ``` ## Signature ```ts /** * How to handle violation of rule * 0 - ignore * 1 - warn * 2 - throw */ type RuleLevel = 0 | 1 | 2; /* * Application of rule * always - positive * never - negative */ type RuleCondition = 'always' | 'never'; /* * Additional, optional options to pass to rule */ type RuleOption = any; /** * Basic complete rule definition */ type PrimitiveRule = [RuleLevel, RuleCondition, RuleOption?]; /* * Async rules are resolved during config lookup. * They can be used to set up linting rules based on e.g. the project fs */ type AsyncRule = Promise; /* * Function rules are executed during config lookup. * They can be used to set up linting rules based on e.g. the project fs */ type FunctionRule = () => PrimitiveRule; /* * Async function rules are executed and awaited during config lookup. * They can be used to set up linting rules based on e.g. the project fs */ type AsyncFunctionRule () => Promise; /* * Polymorphic rule struct */ type Rule = PrimitiveRule | FunctionRule | AsyncFunctionRule; /* * Parser preset for conventional commits */ type ParserPreset = { name: string; path: string; opts: any; }; type Seed = { /* * ids resolvable from cwd or configuration file. * Imported and merged into configuration * with increasing precedence, with top level config taking the highest. */ extends?: string[]; /* * id resolvable from cwd or configuration file. * Imported and expanded to {ParserPreset}. * Top level parserPresets override presets in extended configuration. */ parserPreset?: string; /** * Initial map of rules to check against */ rules?: {[ruleName: string]: Rule}; /** * URL to print as help for reports with problems */ helpUrl?: string; }; type Config = { /* * Relatives path to all extended configurations. */ extends: string[]; /* * Expanded parser preset, if any */ parserPreset?: ParserPreset; /* * Merged map of rules to check against */ rules: {[ruleName: string]: Rule}; /** * URL to print as help for reports with problems */ helpUrl?: string; }; type LoadOptions = { /* * Path to the config file to load. */ file?: string; /* * The cwd to use when loading config from file parameter. */ cwd: string; }; load(seed: Seed = {}, options?: LoadOptions = {cwd: process.cwd()}) => Promise; ``` ## Import ```js import load from "@commitlint/load"; ``` ## Examples ### Inline rules ```js const config = await load({ rules: { "body-leading-blank": [2, "always"], }, }); console.log(config); // => { extends: [], rules: { 'body-leading-blank': [ 2, 'always' ] } } ``` ### Reference a file ```js const config = await load({ extends: ["./package"] }); console.log(config); // => { extends: ['./package', './package-b'], rules: {} } ``` ### Inline `parserPreset` ```js const config = await load({ parserPreset: "./parser-preset.js" }); console.log(config); /* => { extends: [], rules: {}, parserPreset: { name: './parser-preset.js', path: './parser-preset.js', opts: {} } } */ ``` ### Config file with with current working directory ```js const config = await load({}, { file: ".commitlintrc.yml", cwd: process.cwd() }); console.log(config); /* => { extends: [], rules: { 'body-leading-blank': [ 1, 'always' ] }, formatter: '@commitlint/format', plugins: {} } */ ``` --- --- url: /api/read.md --- # @commitlint/read Read commit messages from a specified range or disk ## Install ```sh npm install --save @commitlint/read ``` ## Signature ```ts type Range = { /* Lower end of the commit range to read */ from: string; /* Upper end of the commit range to read */ to: string; /* Whether (boolean) to read from ./.git/COMMIT_EDITMSG or where to read from (string) */ edit?: boolean | string; }; read(range: Range) => Promise ``` ## Import ```js import read from "@commitlint/read"; ``` ## Examples Consider to have a repository with two commits: 1. Initial commit 2. I did something ### Using `edit: true` ```js const result = await read({ edit: true }); console.info(result); // => ['I did something\n\n'] ``` ### Read last two commits ```js const result = await read({ from: "HEAD~2" }); console.info(result); // => ['I did something\n\n', 'Initial commit\n\n'] ``` ### Read commits within a range ```js const result = await read({ from: "HEAD~2", to: "HEAD~1" }); console.info(result); // => ['Initial commit\n\n'] ``` ### Read commit message from git gui file ```js const result = await read({ edit: "./git/GITGUI_EDITMESSAGE" }); console.info(result); // => ['I did something via git gui\n\n'] ``` --- --- url: /api/lint.md --- # @commitlint/lint Lint a string against commitlint rules ## Install ```sh npm install --save @commitlint/lint ``` ## Signature ```ts type RuleLevel = 0 | 1 | 2; type RuleCondition = 'always' | 'never'; type RuleOption = any; type PrimitiveRule = [RuleLevel, RuleCondition, RuleOption?]; type AsyncRule = Promise; type FunctionRule = () => PrimitiveRule; type AsyncFunctionRule = () => Promise; type Rule = PrimitiveRule | FunctionRule | AsyncFunctionRule; type Problem = { level: number; valid: boolean; name: string; message: string; } type Report = { valid: boolean; errors: Problem[]; warnings: Problem[]; } type Options = { parserOpts?: any; }; lint(message: string, rules: {[ruleName: string]: Rule}, opts?: Options) => Promise; ``` ## Basic Examples ### Import ```js import lint from "@commitlint/lint"; ``` ### Usage without config ```js const report = await lint("foo: bar"); console.log(report); // => { valid: true, errors: [], warnings: [] } ``` ### Usage with type-enum rules and valid message ```js const report = await lint("foo: bar", { "type-enum": [1, "always", ["foo"]] }); console.log(report); // => { valid: true, errors: [], warnings: [] } ``` ### Usage with type-enum rules and invalid message ```js const report = await lint("foo: bar", { "type-enum": [1, "always", ["bar"]] }); console.log(report); /* => { valid: false, errors: [], warnings: [ { level: 1, valid: false, name: 'type-enum', message: 'type must be one of [bar]' } ] } */ ``` ### Usage with custom parser options ```js const opts = { parserOpts: { headerPattern: /^(\w*)-(\w*)/, headerCorrespondence: ["type", "scope"], }, }; const report = await lint("foo-bar", { "type-enum": [2, "always", ["foo"]] }, opts); console.log(report); // => { valid: true, errors: [], warnings: [] } ``` ## Load configuration ```js import load from "@commitlint/load"; import lint from "@commitlint/lint"; const CONFIG = { extends: ["@commitlint/config-conventional"], }; const opts = await load(CONFIG); const report = await lint( "foo: bar", opts.rules, opts.parserPreset ? { parserOpts: opts.parserPreset.parserOpts } : {}, ); console.log(report); /* => { valid: false, errors: [ { level: 2, valid: false, name: 'type-enum', message: 'type must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test]' } ], warnings: [] } */ ``` ## Read git history ```js import lint from "@commitlint/lint"; import read from "@commitlint/read"; const RULES = { "type-enum": [2, "always", ["foo"]], }; const commits = await read({ to: "HEAD", from: "HEAD~2" }); console.info(commits.map((commit) => lint(commit, RULES))); ``` ## Simplified last-commit checker ```js import load from "@commitlint/load"; import read from "@commitlint/read"; import lint from "@commitlint/lint"; const { rules, parserPreset } = load(); const [commit] = await read({ from: "HEAD~1" }); const report = await lint( commit, rules, parserPreset ? { parserOpts: parserPreset.parserOpts } : {}, ); console.log(JSON.stringify(result.valid)); ``` --- --- url: /api/format.md --- # @commitlint/format Format commitlint reports ## Install ```sh npm install --save @commitlint/format ``` ## Signature ```ts type Problem = { /* * Level of the problem hint | warning | error */ level: 0 | 1 | 2; /* * Name of the problem to annotate the message with */ name: string; /* * Message to print */ message: string; } type Report = { results: ReportResult[]; } type ReportResult = { errors: Problem[]; warnings: Problem[]; } type formatOptions = { /** * Color the output **/ color: boolean = true; /** * Signs to use as decoration for messages with severity 0, 1, 2 **/ signs: readonly [string; string; string] = [' ', '⚠', '✖']; /** * Colors to use for messages with severity 0, 1, 2 **/ colors: readonly [string; string; string] = ['white', 'yellow', 'red']; /** * Print summary and inputs for reports without problems **/ verbose: boolean = false; /** * URL to print as help for reports with problems **/ helpUrl: string; } format(report?: Report = {}, options?: formatOptions = {}) => string[]; ``` ## Import ```js import format from "@commitlint/format"; ``` ## Examples ### Empty usage (no error founds with colors) ```js format(); /* => [ '\u001b[1m\u001b[32m✔\u001b[39m found 0 problems, 0 warnings\u001b[22m' ] */ ``` ### Without colors ```js format( { results: [ { warnings: [ { level: 0, name: "some-hint", message: "This will not show up as it has level 0", }, { level: 1, name: "some-warning", message: "This will show up yellow as it has level 1", }, ], errors: [ { level: 2, name: "some-error", message: "This will show up red as it has level 2", }, ], }, ], }, { color: false, }, ); /* => [ '✖ This will show up red as it has level 2 [some-error]', ' This will not show up as it has level 0 [some-hint]', '⚠ This will show up yellow as it has level 1 [some-warning]', '✖ found 1 problems, 2 warnings' ] */ ``` --- --- url: /concepts/commit-conventions.md --- # Concept: Commit conventions [Commit conventions](https://www.conventionalcommits.org/) allow your team to add more semantic meaning to your git history. This e.g. includes `type`, `scope` or `breaking changes`. With this additional information tools can derive useful human-readable information for releases of your project. Some examples are * Automated, rich changelogs * Automatic version bumps * Filter for test harnesses to run The most common commit conventions follow this pattern: ```text type(scope?): subject body? footer? ``` ## Multiple scopes Commitlint supports multiple scopes. Segments may be separated using delimiters (default: `/`, `\`, `,`). The set of allowed delimiters can be customized via the [scope-delimiter-style](/reference/rules.html#scope-delimiter-style) rule. --- --- url: /concepts/shareable-config.md --- # Concept: Shareable configuration Most commonly shareable configuration is delivered as npm package exporting an object containing `.rules` as default. To use shared configuration you specify it as item in the `.extends` array: ::: code-group ```js [commitlint.config.js] /** * @type {import('@commitlint/types').UserConfig} */ export default { extends: ["example"], // => commitlint-config-example }; ``` ::: This causes `commitlint` to pick up `commitlint-config-example`. Make it available by installing it. ```sh npm install --save-dev commitlint-config-example ``` The rules found in `commitlint-config-example` are merged with the rules in `commitlint.config.js`, if any. This works recursively, enabling shareable configuration to extend on an indefinite chain of other shareable configurations. ## Relative config You can also load local configuration by using a relative path to the file. ::: warning This must always start with a `.` (dot). ::: ::: code-group ```js [commitlint.config.js] export default { extends: ["./example"], // => ./example.js }; ``` ::: ## Scoped packages When using scoped packages you have two options. You can provide the full path of the package like: ::: code-group ```js [commitlint.config.js] export default { extends: ["@commitlint/config-conventional"], // => @commitlint/config-conventional }; ``` ::: Or just the scope/owner of the package. > \[!TIP] > Just like "normal" extends listed above, this will add `/commitlint-config`. ::: code-group ```js [commitlint.config.js] export default { extends: ["@coolcompany"], // => @coolcompany/commitlint-config }; ``` ::: If you don't use the exact `/commitlint-config` pattern, you have to provide the full name of the package. --- --- url: /support/troubleshooting.md --- # Troubleshooting ## Getting `Range error: Found invalid rule names: [...]` after update {#range-error-invalid-rule} After updating one or more `@commitlint` packages you might encounter an error like: ```text Found invalid rule names: header-trim. Supported rule names are: body-case, body-empty, ... ``` The source of this error is likely a mismatch of version between `@commitlint` packages in `node_modules`. E.g.: you might have a config requesting a rule that is not included in `@commitlint/rules`. > \[!TIP] > If you are relying on a config which depends on an earlier version of `@commitlint/config-conventional` be sure to update them: > > ```sh > npm update @commitlint/config-conventional > ``` *** > \[!NOTE] > Detailed explanation about the error can be found in this [comment](https://github.com/conventional-changelog/commitlint/pull/3871#issuecomment-1911455325). --- --- url: /support/releases.md --- # Releases Security patches will be applied to versions which are not yet EOL.\ Features will only be applied to the current main version. We're not a sponsored OSS project. Therefore we can't promise that we will release patch versions for older releases in a timely manner.\ If you are stuck on an older version and need a security patch we're happy if you can provide a PR. For a list of releases check our [README](https://github.com/conventional-changelog/commitlint#releases) --- --- url: /support/upgrade.md --- # Upgrade Guides ## validate-commit-msg The maintainers of [validate-commit-msg](https://github.com/conventional-changelog-archived-repos/validate-commit-msg) have deprecated their package in favor of `commitlint`. ### Migrating with default settings The most common `validate-commit-msg` use cases can be recreated with minor changes to your setup. **Replace validate-commit-msg with commitlint** ```sh npm remove validate-commit-msg --save-dev npm install --save-dev @commitlint/cli @commitlint/config-conventional ``` **Add a commitmsg run-script to package.json** ``` { "scripts": { "commitmsg": "commitlint -x @commitlint/config-conventional -E GIT_PARAMS" } } ``` **Install husky** ```sh npm install --save-dev husky ``` ### Migrating with custom settings If you used `validate-commit-msg` with custom configuration you might want to customize `commitlint` configuration, too. **Replace validate-commit-msg with commitlint** ```sh npm remove validate-commit-msg --save-dev npm install --save-dev @commitlint/cli @commitlint/config-conventional ``` **Add a commitmsg run-script to package.json** ``` { "scripts": { "commitmsg": "commitlint -E GIT_PARAMS" } } ``` **Install husky** ```sh npm install --save-dev husky ``` **Configure commitlint** ```js module.exports = { extends: ["@commitlint/config-conventional"], rules: { // Place your rules here "scope-enum": [2, "always", ["a", "b"]], // error if scope is given but not in provided list }, }; ``` ### validate-commit-msg option equivalents ```js { "types": ["a", "b"], // 'type-enum': [2, 'always', ['a', 'b']] "scope": { "required": true, // 'scope-empty': [2, 'never'] "allowed": ["a", "b"], // 'scope-enum': [2, 'always', ['a', 'b']]; specify [0] for allowed: ["*"] "validate": false, // 'scope-enum': [0], 'scope-empty': [0] "multiple": false // multiple scopes are not supported in commitlint }, "warnOnFail": false, // no equivalent setting in commitlint "maxSubjectLength": 100, // 'header-max-length': [2, 'always', 100] "subjectPattern": ".+", // may be configured via `parser-preset`, contact us "subjectPatternErrorMsg": "msg", // no equivalent setting in commitlint "helpMessage": "", // no equivalent setting in commitlint "autoFix": false // no equivalent setting in commitlint } ``` Refer to the [Rules Reference](/reference/rules) for a list of all available configuration options. There is also the [#commitlint](https://node-tooling.slack.com/messages/C7M8XJ4RL/) channel on the DevTools Slack workspace. Join us there and we'll do our best to help you with your migration. ## Version 1 to 2 ```bash npm install --save-dev conventional-changelog-lint@latest ``` ### Breaking changes #### CLI * None #### Config * **wildcards** config is ignored - as of version `2.0.0` the former `.wildcards` configuration is ignored entirely. If your `.conventional-changelog-lintrc`, `commitlint.config.js` or an extended shareable configuration has a `.wildcards` key a warning will be issued. #### API * None ## Version 2 to 3 ```bash npm remove --save-dev conventional-changelog-lint npm install --save commitlint mv .conventional-changelog-lintrc commitlint.config.js ``` * Rename all calls to `conventional-changelog-lint` to `commitlint` ### Breaking changes #### CLI * `conventional-changelog-lint` command now is called `commitlint` * `commitlint` command now is installed via `@commitlint/cli` * `.conventional-changelog-lintrc` now is called `commitlint.config.js` * `commitlint` does not search upwards in the directory structure for config * `--preset | -p` flag was removed. The `angular` preset is used always. #### Config * `.preset` key is removed. The `angular` preset is used always. #### API * `getConfiguration(name, settings, seed)` changed to `load(seed)` * `getMessages(range)` changed to `read(range)` * `getPreset(name, require)` removed * `format(report, options)` now only respects `.color` on `options` * `lint(message, options)` changed to `lint(message, rules)` ## Version 4 to 5 ```bash npm remove --save-dev @commitlint/config-angular npm install --save @commitlint/cli @commitlint/config-conventional echo 'module.exports = {extends: ["@commitlint/config-conventional"]};'; ``` ### Breaking changes #### Config * `config-angular` dropped support for the `chore` type, breaking compatibility with conventional-changelog, use `config-conventional` instead. ## Version 7 to 8 ### Breaking changes #### Output on successful commit will be omitted * You can use the `--verbose` flag to get positive output ## Version 8 to 9 ### Breaking changes #### Possible types * `improvement` type will now be rejected by this config ## Version 9 to 10 ### Breaking changes #### Node support * node v8 is not supported anymore ## Version 10 to 11 ### Breaking changes #### Lerna support * lerna v2 is not supported anymore ## Version 11 to 12 ### Breaking changes #### resolve-extends * The order of the `extends` resolution is changed from right-to-left to left-to-right --- --- url: /attributions.md --- # Attributions `commitlint` is possible because of the hard work of the folks of the `conventional-changelog` project *** Thanks [@markusoelhafen](https://github.com/markusoelhafen) for providing the `commitlint` icon *** Homepage SVG Demo generated with [svg-term-cli](https://github.com/marionebl/svg-term-cli)