fix(load): resolve async config exports in CJS projects#4659
Conversation
Config files that export a Promise or async/sync function are now resolved before validation. This enables CJS projects to use patterns like `module.exports = createPreset()` (returning a Promise) or `module.exports = async () => config`. Closes conventional-changelog#4557
Review Summary by QodoResolve async config exports in CJS projects
WalkthroughsDescription• Resolve async config exports before validation in CJS projects • Support Promise and async function config exports • Add test fixtures for async config patterns Diagramflowchart LR
A["Config loaded from file"] --> B{"Is function?"}
B -->|Yes| C["Call function"]
B -->|No| D["Await Promise"]
C --> E["Await result"]
D --> F["Resolved config"]
E --> F
F --> G["Validate config"]
File Changes1. @commitlint/load/src/load.ts
|
Code Review by Qodo
1. Async extends not resolved
|
|
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
This PR fixes @commitlint/load so that when a CommonJS config exports a Promise or a (async) function, the loader resolves it to a plain config object before calling validateConfig, ensuring validation and the rest of the load pipeline work correctly.
Changes:
- Resolve loaded config exports by calling function exports and awaiting Promise/function results before validation.
- Add test coverage for CJS configs exporting a Promise and an async function.
- Add new fixtures covering both async export patterns.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| @commitlint/load/src/load.ts | Resolves loaded.config (call if function, then await) before validation and merge. |
| @commitlint/load/src/load.test.ts | Adds tests asserting async config exports are resolved into rules correctly. |
| @commitlint/load/fixtures/async-config-promise/package.json | New fixture package metadata for Promise-export config. |
| @commitlint/load/fixtures/async-config-promise/commitlint.config.cjs | New fixture exporting Promise.resolve({...}). |
| @commitlint/load/fixtures/async-config-function/package.json | New fixture package metadata for function-export config. |
| @commitlint/load/fixtures/async-config-function/commitlint.config.cjs | New fixture exporting async () => ({...}). |
💡 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.
Problem
When a CJS config exports a Promise (
module.exports = createPreset()wherecreatePresetis async) or a function (module.exports = async () => config),@commitlint/loadpasses the raw Promise/function tovalidateConfiginstead of the resolved config object.Fix
In
load.ts, resolve the loaded config before validation: if it's a function, call it; then await the result. Plain objects pass through unchanged.This works because
@commitlint/loadalready runs in an async context. The raw export from cosmiconfig was passed directly tovalidateConfigwithout resolving it first. By awaiting the value (and calling it if it's a function), the resolved config object reaches validation and the rest of the pipeline correctly.Tests
Promise.resolve({...})resolves correctlyasync () => ({...})resolves correctlyFixes #4557
Closes #4557