Skip to content

Resolver: Fall through unmatched nested conditions - #2074

Merged
robhogan merged 3 commits into
react:mainfrom
maximilliangrand:fix/nested-export-condition-fallback
Oct 17, 2026
Merged

robhogan merged 3 commits into
react:mainfrom
maximilliangrand:fix/nested-export-condition-fallback

Conversation

@maximilliangrand

Copy link
Copy Markdown
Contributor

Summary

Metro stops resolving conditions after entering a matching parent, even when none of that parent's nested conditions match. For example, with react-native asserted and a CommonJS import, this export fails instead of selecting ./fallback.js:

{
  "exports": {
    "./feature": {
      "react-native": {"import": "./feature.mjs"},
      "default": "./fallback.js"
    }
  }
}

Resolve nested conditions recursively and continue to the next parent condition only when the nested object has no matching target. This follows Node's nested-condition semantics and applies to both exports and imports. Explicit null exclusions and invalid targets stop resolution; unmatched exact subpaths retain precedence over wildcard patterns. Numeric and boolean targets report invalid package configuration, and unsupported nested arrays retain their blocked behavior.

Changelog: [Fix] Resolve parent fallback conditions when a nested package exports or imports condition has no matching target

AI-assisted implementation and testing.

Test plan

On macOS with Node.js 26.7.0 and the frozen Yarn lockfile:

  • Before the fix, the six new parent-fallback regressions failed against upstream; the existing tests and null/invalid-string controls passed.
  • node node_modules/jest/bin/jest.js packages/metro-resolver/src/__tests__/package-exports-test.js packages/metro-resolver/src/__tests__/package-imports-test.js --runInBand — all 77 tests passed. Cases cover unmatched, empty, and deeper nested objects; later named/default conditions; null exclusions; invalid strings/numbers/booleans; and unsupported nested arrays.
  • node node_modules/jest/bin/jest.js packages/metro-resolver --runInBand — 145 tests and 14 snapshots passed.
  • node node_modules/jest/bin/jest.js --ci --maxWorkers 4 — 158 suites, 2,956 tests, and 487 snapshots passed; 14 tests skipped.
  • node node_modules/flow-bin/cli.js check --timeout 90 — no errors.
  • node node_modules/eslint/bin/eslint.js . --cache and node node_modules/prettier/bin/prettier.cjs --check . — passed.
  • yarn run build, yarn run typecheck-ts, and yarn run test-smoke — passed.

The fallback, null, and invalid-target expectations were also checked against Node.js resolution using temporary packages. Node supports additional array shapes that Metro already treats as unsupported; this change preserves Metro's existing array behavior. Windows/Linux and other Node versions were not run locally.

@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 Oct 16, 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 Oct 16, 2026
Metro treats `null` and `undefined` as equivalent, so signalling an unmatched nested condition with `undefined` (as distinct from an explicit `null` exclusion) needed a `lint/strictly-null` suppression. Return a module-level `NO_MATCH` symbol instead. Unlike a string sentinel, it can't collide with a literal target in `package.json`.

At the top level, any non-string result is stored as `null`, so an exact key with no matching condition is still retained and takes precedence over subpath patterns.

Changelog: [Internal]
@robhogan

Copy link
Copy Markdown
Collaborator

Looks good, thanks @maximilliangrand !

I've pushed a couple of small changes:

  • Metro's convention doesn't distinguish null and undefined so I've swapped undefined for a NO_MATCH local symbol.
  • preferred test.each so it's clearer which cases fail if there's ever a regression.

A possible follow-up is the still-incorrect handling of arrays - if we support them at all we should align with Node.

Thanks again

@robhogan
robhogan merged commit bd9104c into react:main Oct 17, 2026
16 checks passed
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.

2 participants