Skip to content

Inline the last duplicate Platform.select key - #1889

Closed
OskarEichler wants to merge 1 commit into
react:mainfrom
OskarEichler:codex/fix-inline-platform-duplicate-keys
Closed

OskarEichler wants to merge 1 commit into
react:mainfrom
OskarEichler:codex/fix-inline-platform-duplicate-keys

Conversation

@OskarEichler

@OskarEichler OskarEichler commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Metro statically replaces Platform.select({...}) for the target platform. Its property scan currently stops at the first matching key, while JavaScript object-literal evaluation keeps the last duplicate definition. For example, Platform.select({ios: 1, ios: 2}) runs as 2 but Metro compiles it to 1.

Scan the already-validated static properties from the end and return the first reverse match. This preserves O(n) behavior, avoids an unnecessary nullable temporary, and keeps compiled output consistent with runtime object semantics. The React Native Babel preset contains a parallel transform; companion React Native PR #58249 applies the same rule there.

Changelog:

 - **[Fix]**: Inline the last duplicate key from static `Platform.select` object literals

Test plan

  • Added a focused metro-transform-plugins regression; it fails on pristine main with 1 and passes with 2 after the fix.
  • All package suites pass: 6/6 suites, 160/160 tests, 41 snapshots.
  • Full Flow check reports no errors.
  • Full monorepo build passes for all 17 packages.
  • Targeted ESLint, Prettier, and git diff --check pass.

No behavior changes for object literals without duplicate static keys.

@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 Aug 30, 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 Aug 30, 2026
@vzaidman

vzaidman commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

@OskarEichler Is this a real legitimate usecase? Could we throw in case duplicate "Platform.select" are used?

  1. I think that introducing such an unreadable and weird code is not worth it.
  2. It seems to me like if a duplicate key is used, it means that the developer who introduced it did it by mistake.
  3. Aren't JS agnostic in regards to the order of keys definitions?

@robhogan

robhogan commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

I don't think this fix is too complicated tbh (it's just iterating in reverse, because later props always overwrite earlier props at runtime - inlining should behave the same).

It seems to me like if a duplicate key is used, it means that the developer who introduced it did it by mistake

Yeah I agree it's niche, but it's a still legit bug that Metro's inlining changes the behaviour. I wouldn't to throw on valid JS - we could bail out of inlining, but detecting duplicates is just as much work as fixing the bug tbh.

The version of the inline plugin in Metro is likely to go away soon though, so the corresponding PR to RN is more important.

@meta-codesync

meta-codesync Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

@vzaidman has imported this pull request. If you are a Meta employee, you can view this in D118499357.

@meta-codesync meta-codesync Bot closed this in c7bd299 Sep 4, 2026
@meta-codesync meta-codesync Bot added the Merged label Sep 4, 2026
@meta-codesync

meta-codesync Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

@vzaidman merged this pull request in c7bd299.

meta-codesync Bot pushed a commit that referenced this pull request Sep 11, 2026
Summary:
Publish Metro 0.87.1 from `main`.

Changelog: [Internal]

Pull Request resolved: #1926

Test Plan:
```
node scripts/updateVersion.js 0.87.1
sl diff --stat
```

Only the 17 `packages/*/package.json` manifests change, and every `0.87.0` reference is replaced.

Draft release notes (from the 91 commits in v0.87.0...main - reverted and internal-only changes omitted):

```
 - **[Feature]**: Add `resolver.schemeResolvers` to resolve URI-scheme-prefixed specifiers (eg `foo:bar`) with custom resolvers (#1804 by robhogan)
 - **[Feature]**: Resolve `metro:babel-runtime/<path>` imports to Metro's own `babel/runtime` dependency (8388f71 by robhogan)
 - **[Feature]**: `metro-file-map`: Add `crawlerFactory` to optionally replace the built-in Watchman / Node crawlers (68022f0 by vzaidman)
 - **[Feature]**: `metro-babel-transformer`: Pass `inlinePlatform` to Babel presets via `caller` (6bbe095 by robhogan)
 - **[Fix]**: Replace `image-size` dependency with vendored parsers, fix CVE alerts (#1860 by robhogan)
 - **[Fix]**: Include `charset=utf-8` in the `Content-Type` of text assets and source files served by the dev server (#1888 by robhogan)
 - **[Fix]**: Preserve `Platform.OS` write targets during constant inlining (#1890 by OskarEichler)
 - **[Fix]**: Inline the last duplicate key from static `Platform.select` object literals (#1889 by OskarEichler)
 - **[Fix]**: `FallbackWatcher` no longer misses files written to a directory while it is being crawled (#1907 by robhogan)
 - **[Fix]**: `HttpStore`: Handle socket errors during writes, so they're retried rather than crashing the process (fad3b88)
 - **[Fix]**: Treat `CI=false` and `CI=0` as not-CI when defaulting `watch`. CI is now detected from `process.env.CI` only, dropping the `ci-info` dependency (61e4592 by robhogan)
 - **[Fix]**: Fix Fast Refresh hitting undefined modules when using lazy (segmented) bundles in dev (c70d0ae by robhogan)
 - **[Performance]**: `FallbackWatcher`: Drop `walker` dependency, reduce crawl RSS by ~34% and peak heap by ~41% (#1906 by robhogan)
 - **[Performance]**: Don't emit redundant empty dependency map arrays for modules with no dependencies (#1858 by robhogan)
 - **[Types]**: Restore publishing of private (underscore-prefixed) fields in TypeScript declarations (#1876 by robhogan)
 - **[Types]**: Fix async functions being inferred as returning `void` (not `Promise<void>`), eg `MetroServer#end` (#1909 by robhogan)
 - **[Types]**: Tidy up generated types, allow nullable props to be omitted in more places (#1885 by robhogan)

> NOTE: Experimental features are not covered by semver and can change at any time.

 - **[Experimental]**: Add `serializer.unstable_inlineDependencyMap` to inline module IDs at serialisation time (#1786 by robhogan)
 - **[Experimental]**: Add `serializer.unstable_getAsyncDependencyPath` to supply custom `paths` to framework-defined `__loadBundleAsync` (#1855 by robhogan)
 - **[Experimental]**: Remove `transformer.unstable_renameRequire` - `require` is never renamed (42577f7 by robhogan)
 - **[Experimental]**: `experimentalImportSupport`: Fix `export * from` re-exporting the source module's default export (#1777 by robhogan)

 **Full Changelog**: v0.87.0...v0.87.1
```

Reviewed By: zeyap

Differential Revision: D119675927

Pulled By: GijsWeterings

fbshipit-source-id: daf17f3501b4725c21c39b4dfd5c82b95e01dca5
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. Merged 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.

3 participants