fix(resolve-extends): always resolve extended parser presets for proper merging#4647
Conversation
…er merging When a user provides a partial parserPreset (e.g. only issuePrefixes), the extended config's string parser preset was skipped because loadExtends checked !context.parserPreset before resolving. This meant the extended preset's headerPattern was never loaded, so it got lost during the config merge. Two changes fix this: 1. Remove the !context.parserPreset guard in loadExtends so string parser presets from extended configs are always resolved, making their parserOpts available for merging. 2. Preserve user-provided properties during the nested parserOpts unwrapping in loadParserOpts. Previously the unwrap replaced the outer object entirely, dropping any user additions that were merged at that level during config resolution. Fixes conventional-changelog#4640
Review Summary by QodoFix parser preset merging with partial user overrides
WalkthroughsDescription• Fix parser preset merging when user provides partial overrides • Always resolve extended string parser presets for proper configuration • Preserve user properties during nested parserOpts unwrapping • Add integration and unit tests for partial preset override scenario Diagramflowchart LR
A["User Config<br/>partial parserPreset"] -->|merge| B["Extended Config<br/>string parserPreset"]
B -->|resolve| C["loadExtends<br/>remove guard"]
C -->|unwrap| D["loadParserOpts<br/>preserve properties"]
D -->|result| E["Merged parserPreset<br/>with all properties"]
File Changes1. @commitlint/resolve-extends/src/index.ts
|
Code Review by Qodo
1. ESM namespace mutation throw
|
|
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. |
There was a problem hiding this comment.
Pull request overview
Fixes commitlint config resolution so that extended configs’ string parserPresets are always resolved and their parserOpts are preserved/merged when users provide partial parserPreset overrides (e.g. issuePrefixes), addressing #4640.
Changes:
- Update
@commitlint/resolve-extendsto always resolve stringparserPresets from extended configs so theirparserOptsparticipate in merging. - Adjust
@commitlint/loadparser opts unwrapping to preserve outer merged properties when encountering nestedparserOpts.parserOpts. - Add unit + integration tests and a fixture to reproduce the reported configuration scenario.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| @commitlint/resolve-extends/src/index.ts | Always resolves string parserPreset in extended configs and assigns the resolved preset onto the extended config object. |
| @commitlint/resolve-extends/src/index.test.ts | Adds a unit test intended to cover partial parserPreset merging behavior. |
| @commitlint/load/src/utils/load-parser-opts.ts | Preserves outer parser options when unwrapping nested parserOpts.parserOpts. |
| @commitlint/load/src/load.test.ts | Adds integration coverage validating merged parser preset options from an extended string preset + user partial override. |
| @commitlint/load/fixtures/parser-preset-partial-user-override/extended/index.js | Fixture extended config exporting a string parserPreset. |
| @commitlint/load/fixtures/parser-preset-partial-user-override/extended/conventional-changelog-custom.js | Fixture parser preset providing headerPattern/headerCorrespondence. |
| @commitlint/load/fixtures/parser-preset-partial-user-override/commitlint.config.js | Fixture user config partially overriding parserPreset.parserOpts.issuePrefixes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
|
Please have a look at the feedback from qodo and copilot, thanks! |
- Shallow-copy the dynamicImport result to avoid mutating an immutable ESM namespace object when assigning the resolved parserPreset. - Add explicit type cast in load-parser-opts destructuring so inner is Record<string, unknown> instead of unknown. - Clarify unit test scope: it covers the merge path while the integration test covers string-to-object resolution.
|
Thanks! |
Problem
When a user provides a partial
parserPresetoverride (e.g. onlyissuePrefixes), the extended config's string parser preset is skipped entirely. This means the preset'sheaderPatternis never resolved, so it gets lost during the config merge.For example, with this config:
The
headerPatternfromconventional-changelog-conventionalcommits(which includes!?for breaking changes) is never loaded. The parser falls back toconventional-changelog-angular's pattern, which lacks!?, causing type detection to fail on commits likefeat!: breaking change.Root cause
Two issues:
loadExtendsin@commitlint/resolve-extendshad a!context.parserPresetguard that skipped resolution of the extended config's stringparserPresetwhenever the user provided any parserPreset object. This prevented the extended preset'sheaderPatternfrom being loaded.loadParserOptsin@commitlint/loadunwrapped nestedparserOpts.parserOptsby replacing the outer object entirely, dropping any user-provided properties (likeissuePrefixes) that were merged at the outer level during config resolution.Fix
Remove the
!context.parserPresetguard so string parser presets from extended configs are always resolved, making theirparserOptsavailable for merging.During the nested
parserOptsunwrapping, preserve outer properties by merging them into the inner object instead of discarding them.Testing
@commitlint/resolve-extendsverifying partial userparserPresetmerges with extended config's preset@commitlint/loadwith a fixture that reproduces the exact scenario from the issueFixes #4640