Skip to content

feat(cli): add --default-config flag to lint without a config file#4805

Merged
escapedcat merged 1 commit into
masterfrom
feat/cli-default-config-flag
Jun 2, 2026
Merged

feat(cli): add --default-config flag to lint without a config file#4805
escapedcat merged 1 commit into
masterfrom
feat/cli-default-config-flag

Conversation

@escapedcat

Copy link
Copy Markdown
Member

When config resolution yields no rules, --default-config falls back to the built-in default config (@commitlint/config-conventional) instead of failing with empty-rules:

echo "feat: add new feature" | npx commitlint --default-config

This makes commitlint usable without any setup, e.g. for one-off checks or server-side pre-receive hooks where installing a shareable config is not practical. A configuration file with rules always takes precedence over the flag, and configs passed via --extends are kept and override the default config when the fallback applies.

The empty-rules error message now also points to the new flag, and the CLI reference docs are re-synced with the actual --help output (which was missing --legacy-output).

Closes #3662

When config resolution yields no rules, --default-config falls back to
the built-in default config (@commitlint/config-conventional) instead
of failing with empty-rules:

    echo "feat: add new feature" | npx commitlint --default-config

This makes commitlint usable without any setup, e.g. for one-off checks
or server-side pre-receive hooks where installing a shareable config is
not practical. A configuration file with rules always takes precedence
over the flag, and configs passed via --extends are kept and override
the default config when the fallback applies.

The empty-rules error message now also points to the new flag, and the
CLI reference docs are re-synced with the actual --help output (which
was missing --legacy-output).

Closes #3662

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@qodo-code-review

Copy link
Copy Markdown

Review Summary by Qodo

Add --default-config flag to lint without configuration file

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add --default-config flag to use built-in conventional config fallback
• Enables commitlint usage without configuration file setup
• Preserves user-supplied --extends configs when fallback applies
• Updates error messages and CLI documentation with new flag
• Adds comprehensive test coverage for default config behavior
Diagram
flowchart LR
  A["CLI invocation with --default-config"] --> B["Load config from file/extends"]
  B --> C{"Rules found?"}
  C -->|Yes| D["Use loaded config"]
  C -->|No| E["Fallback to @commitlint/config-conventional"]
  E --> F["Merge with user extends"]
  F --> G["Lint commit message"]
  D --> G

Loading

Grey Divider

File Changes

1. @commitlint/cli/src/cli.ts ✨ Enhancement +47/-12

Implement --default-config flag and fallback logic

@commitlint/cli/src/cli.ts


2. @commitlint/cli/src/cli.test.ts 🧪 Tests +92/-22

Add comprehensive tests for default config behavior

@commitlint/cli/src/cli.test.ts


3. @commitlint/cli/src/types.ts ✨ Enhancement +1/-0

Add default-config flag to CliFlags interface

@commitlint/cli/src/types.ts


View more (6)
4. @commitlint/cli/package.json Dependencies +1/-0

Add @commitlint/config-conventional as dependency

@commitlint/cli/package.json


5. @commitlint/cli/fixtures/no-config/package.json 🧪 Tests +4/-0

Create test fixture without config file

@commitlint/cli/fixtures/no-config/package.json


6. @commitlint/cli/fixtures/no-config/helpurl-only.js 🧪 Tests +5/-0

Create shareable config fixture for testing extends

@commitlint/cli/fixtures/no-config/helpurl-only.js


7. docs/guides/local-setup.md 📝 Documentation +9/-0

Document --default-config flag usage example

docs/guides/local-setup.md


8. docs/reference/cli.md 📝 Documentation +55/-31

Update CLI reference with new flag and usage guide

docs/reference/cli.md


9. pnpm-lock.yaml Dependencies +3/-0

Update lock file with new dependency

pnpm-lock.yaml


Grey Divider

Qodo Logo

@qodo-code-review

qodo-code-review Bot commented Jun 2, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (1)

Context used

Grey Divider


Action required

1. --default-config gates default fallback 📎 Requirement gap ≡ Correctness
Description
The CLI only loads the built-in default config when --default-config is explicitly provided, so
running commitlint with no config file still resolves to empty rules and errors. This violates the
requirement to automatically use the built-in default configuration when no commitlint config file
is found.
Code

@commitlint/cli/src/cli.ts[R495-505]

Evidence
PR Compliance ID 1 requires that when no commitlint config file exists, the CLI automatically loads
a built-in default config instead of erroring with empty rules. The new loadConfig()
implementation only performs the fallback when flags["default-config"] is true, and the added test
explicitly asserts failure without the flag in a no-config fixture, demonstrating the non-compliant
behavior.

Use built-in default commitlint configuration when no commitlint config file is found
@commitlint/cli/src/cli.ts[495-505]
@commitlint/cli/src/cli.test.ts[217-223]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The built-in default config is only applied when the user passes `--default-config`, but the compliance requirement expects commitlint to automatically fall back to the built-in default configuration when no commitlint config file is present.

## Issue Context
`loadConfig()` currently checks `flags["default-config"]` before prepending `@commitlint/config-conventional`, so config-less invocations still end up with `loaded.rules` empty and hit the empty-rules error path.

## Fix Focus Areas
- @commitlint/cli/src/cli.ts[495-505]
- @commitlint/cli/src/cli.test.ts[217-231]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Config loaded twice 🐞 Bug ➹ Performance
Description
When --default-config is set and the initially resolved config has no rules, loadConfig() calls
@commitlint/load twice, which re-reads and re-executes JS config (including function exports) and
re-imports extends configs a second time. This is unnecessary work and can duplicate side effects
in the edge case where a config file exists but yields empty rules.
Code

@commitlint/cli/src/cli.ts[R489-505]

Evidence
loadConfig() performs two load() calls in the fallback path, and @commitlint/load executes the
loaded config export (including function exports) each time load() runs, so a second load()
repeats work/side effects.

@commitlint/cli/src/cli.ts[489-505]
@commitlint/load/src/load.ts[47-56]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`loadConfig()` performs an initial `load()` to detect `rules` emptiness, then performs a second `load()` when `--default-config` fallback applies. Since `@commitlint/load` evaluates the loaded config export (including function exports), the fallback path can execute the same config twice.

## Issue Context
This primarily affects the case where a config file is present but yields no rules (e.g. `rules: {}` or only non-rule fields). In that situation, the config and its `extends` chain can be evaluated twice.

## Fix Focus Areas
- @commitlint/cli/src/cli.ts[489-505]
- @commitlint/load/src/load.ts[47-56]

## Suggested fix direction
Refactor the fallback to avoid re-reading/re-executing the user config on the second pass. One practical approach:
1. First `load()` as today.
2. If fallback applies, run a second `load()` **without** the user config file (e.g., omit `file` / pass `file: undefined`) to load only the default config (+ CLI `--extends`), then merge its `rules` (and any other needed defaults) into the initially-loaded config, keeping the initially-loaded non-rule fields (helpUrl/parserPreset/plugins/formatter/etc.) as the authoritative values.

This preserves the feature behavior while ensuring the user config is evaluated only once.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds an opt-in CLI fallback that makes @commitlint/cli usable without any local configuration by introducing --default-config, which loads the built-in conventional ruleset when config resolution yields no rules.

Changes:

  • Add --default-config flag that falls back to @commitlint/config-conventional when resolved rules are empty, while preserving --extends precedence.
  • Improve the empty-rules guidance message to mention the new flag.
  • Re-sync CLI reference docs with --help output and add documentation for linting without a config; add fixtures + tests for the new behavior.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated no comments.

Show a summary per file
File Description
@commitlint/cli/src/cli.ts Adds --default-config option, implements loadConfig() fallback logic, reuses module-resolution logic, and updates empty-rules messaging.
@commitlint/cli/src/types.ts Extends CLI flags typing to include "default-config".
@commitlint/cli/src/cli.test.ts Adds coverage for no-config scenarios, fallback behavior, precedence rules, and --print-config with fallback.
@commitlint/cli/package.json Adds workspace dependency on @commitlint/config-conventional to ensure fallback is available.
@commitlint/cli/fixtures/no-config/package.json Adds a no-config fixture for tests.
@commitlint/cli/fixtures/no-config/helpurl-only.js Adds a shareable config fixture with helpUrl but no rules to test fallback + extends behavior.
docs/reference/cli.md Updates CLI help output, documents --default-config, and adds a “Lint without a config file” section.
docs/guides/local-setup.md Adds a tip showing how to try commitlint without a config using --default-config.
pnpm-lock.yaml Updates lockfile for the new CLI dependency.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +495 to +505
// `--default-config` falls back to the built-in default config when
// config resolution yields no rules (e.g. no config file was found).
// The default config is prepended so user-supplied --extends configs
// keep precedence over it.
if (flags["default-config"] && Object.keys(loaded.rules).length === 0) {
const extendsWithDefault = [resolveDefaultConfig(flags), ...(flags.extends || [])];
return load(getSeed({ ...flags, extends: extendsWithDefault }), {
cwd: flags.cwd,
file: flags.config,
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. --default-config gates default fallback 📎 Requirement gap ≡ Correctness

The CLI only loads the built-in default config when --default-config is explicitly provided, so
running commitlint with no config file still resolves to empty rules and errors. This violates the
requirement to automatically use the built-in default configuration when no commitlint config file
is found.
Agent Prompt
## Issue description
The built-in default config is only applied when the user passes `--default-config`, but the compliance requirement expects commitlint to automatically fall back to the built-in default configuration when no commitlint config file is present.

## Issue Context
`loadConfig()` currently checks `flags["default-config"]` before prepending `@commitlint/config-conventional`, so config-less invocations still end up with `loaded.rules` empty and hit the empty-rules error path.

## Fix Focus Areas
- @commitlint/cli/src/cli.ts[495-505]
- @commitlint/cli/src/cli.test.ts[217-231]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@escapedcat escapedcat merged commit 7af27ba into master Jun 2, 2026
15 checks passed
@escapedcat escapedcat deleted the feat/cli-default-config-flag branch June 2, 2026 13:17
sys-support pushed a commit to 3caravelle/renovate that referenced this pull request Jun 25, 2026
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [@commitlint/cli](https://commitlint.js.org/) ([source](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli)) | [`21.0.1` → `21.1.0`](https://renovatebot.com/diffs/npm/@commitlint%2fcli/21.0.1/21.1.0) | ![age](https://developer.mend.io/api/mc/badges/age/npm/@commitlint%2fcli/21.1.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@commitlint%2fcli/21.0.1/21.1.0?slim=true) |
| [@commitlint/config-conventional](https://commitlint.js.org/) ([source](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional)) | [`21.0.1` → `21.1.0`](https://renovatebot.com/diffs/npm/@commitlint%2fconfig-conventional/21.0.1/21.1.0) | ![age](https://developer.mend.io/api/mc/badges/age/npm/@commitlint%2fconfig-conventional/21.1.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@commitlint%2fconfig-conventional/21.0.1/21.1.0?slim=true) |

---

### Release Notes

<details>
<summary>conventional-changelog/commitlint (@&#8203;commitlint/cli)</summary>

### [`v21.1.0`](https://github.com/conventional-changelog/commitlint/blob/HEAD/@&#8203;commitlint/cli/CHANGELOG.md#2110-2026-06-23)

[Compare Source](conventional-changelog/commitlint@v21.0.2...v21.1.0)

##### Features

- **cli:** add --default-config flag to lint without a config file ([#&#8203;4805](conventional-changelog/commitlint#4805)) ([7af27ba](conventional-changelog/commitlint@7af27ba)), closes [#&#8203;3662](conventional-changelog/commitlint#3662)

#### [21.0.2](conventional-changelog/commitlint@v21.0.1...v21.0.2) (2026-05-29)

##### Bug Fixes

- disallow same commit hash for --from and --to ([#&#8203;4773](conventional-changelog/commitlint#4773)) ([121005e](conventional-changelog/commitlint@121005e))

#### [21.0.1](conventional-changelog/commitlint@v21.0.0...v21.0.1) (2026-05-12)

**Note:** Version bump only for package [@&#8203;commitlint/cli](https://github.com/commitlint/cli)

### [`v21.0.2`](https://github.com/conventional-changelog/commitlint/blob/HEAD/@&#8203;commitlint/cli/CHANGELOG.md#2102-2026-05-29)

[Compare Source](conventional-changelog/commitlint@v21.0.1...v21.0.2)

##### Bug Fixes

- disallow same commit hash for --from and --to ([#&#8203;4773](conventional-changelog/commitlint#4773)) ([121005e](conventional-changelog/commitlint@121005e))

</details>

<details>
<summary>conventional-changelog/commitlint (@&#8203;commitlint/config-conventional)</summary>

### [`v21.1.0`](https://github.com/conventional-changelog/commitlint/blob/HEAD/@&#8203;commitlint/config-conventional/CHANGELOG.md#2110-2026-06-23)

[Compare Source](conventional-changelog/commitlint@v21.0.2...v21.1.0)

**Note:** Version bump only for package [@&#8203;commitlint/config-conventional](https://github.com/commitlint/config-conventional)

#### [21.0.2](conventional-changelog/commitlint@v21.0.1...v21.0.2) (2026-05-29)

**Note:** Version bump only for package [@&#8203;commitlint/config-conventional](https://github.com/commitlint/config-conventional)

#### [21.0.1](conventional-changelog/commitlint@v21.0.0...v21.0.1) (2026-05-12)

**Note:** Version bump only for package [@&#8203;commitlint/config-conventional](https://github.com/commitlint/config-conventional)

### [`v21.0.2`](https://github.com/conventional-changelog/commitlint/blob/HEAD/@&#8203;commitlint/config-conventional/CHANGELOG.md#2102-2026-05-29)

[Compare Source](conventional-changelog/commitlint@v21.0.1...v21.0.2)

**Note:** Version bump only for package [@&#8203;commitlint/config-conventional](https://github.com/commitlint/config-conventional)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi44MS4zIiwidXBkYXRlZEluVmVyIjoiNDIuODEuMyIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Reviewed-on: https://git.3caravelle.net/3Caravelle/renovate/pulls/37
Co-authored-by: Renovate Bot <renovate-bot@3caravelle.com>
Co-committed-by: Renovate Bot <renovate-bot@3caravelle.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

feat: built-in default config when no commitlint.config is found

2 participants