Skip to content

Fix mergeConfig dropping state when a config in the chain is async#1754

Open
durvesh1992 wants to merge 1 commit into
react:mainfrom
durvesh1992:fix/mergeconfig-async-trailing-configs
Open

Fix mergeConfig dropping state when a config in the chain is async#1754
durvesh1992 wants to merge 1 commit into
react:mainfrom
durvesh1992:fix/mergeconfig-async-trailing-configs

Conversation

@durvesh1992

Copy link
Copy Markdown

Summary

When mergeConfig(base, ...configs) encounters a config that resolves to a Promise (e.g. an async metro.config.js / a config function returning a promise) and at least one more config follows it, it hands off to mergeConfigAsync with two defects:

  1. Missing spread. mergeConfigAsync(baseConfig, ...reversedConfigs) takes a rest parameter, but the call passed reversedConfigs.toReversed() as a single array argument. The remaining configs end up wrapped one level too deep, so they're merged as a bogus object (producing junk keys like "0") instead of being applied as configs.
  2. Dropped accumulator. currentConfig (the base merged with every config to the left of the async one) was discarded — the unresolved promise was passed as the base, so all prior merges were lost.

The fix spreads the remaining configs into the rest parameter and folds currentConfig into the resolved async config so both the accumulated state and the trailing overrides are preserved.

Test plan

The existing mergeConfig tests only cover sync configs, so this path was uncovered. Added a regression test (an async config function followed by a trailing override) asserting the base, the async config, and the trailing override all survive:

yarn jest packages/metro-config/src/__tests__/mergeConfig-test.js
# Tests: 19 passed  (full metro-config package: 40 passed)
  • Before: result.server.port is undefined (base dropped); trailing override mangled.
  • After: all three configs merge correctly.

When a config (function returning a Promise) appears mid-chain,
mergeConfig handed off to mergeConfigAsync with two bugs:

- It passed reversedConfigs.toReversed() as a single argument instead of
  spreading it into the rest parameter, so the remaining configs were
  wrapped in an extra array and merged as junk (e.g. an index key '0').
- It passed the unresolved promise as the base, discarding currentConfig
  (the base merged with every config to the left of the async one).

Spread the remaining configs and fold currentConfig into the resolved
async config so prior merges and trailing overrides are both preserved.

Adds a regression test (async config function followed by an override)
that fails before and passes after.
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 29, 2026
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Jun 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant