Skip to content

CSS entry module is not wrapped with Expo tree shaking: Property 'require' doesn't exist #696

Closed

Description

@peterferguson

What happened?

With Expo's tree shaking flags (EXPO_UNSTABLE_METRO_OPTIMIZE_GRAPH=1 EXPO_UNSTABLE_TREE_SHAKING=1), an iOS Release build crashes at launch:

ReferenceError: Property 'require' doesn't exist

The exported bundle has the uniwind CSS entry module at top level, outside a __d(...) module wrapper. Its require('uniwind') is not rewritten to r(d[0]):

var{Uniwind:Uniwind}=require('uniwind');Uniwind.__reinit(t=>({...}),['light','dark'],'<fingerprint>',1,[2]);

Without the flags, the same module is wrapped:

__d(function(g,r,i,a,m,e,d){var{Uniwind:o}=r(d[0]);o.__reinit(...)},1,[2]);

What we found:

  • The uniwind Metro transformer replaces the CSS entry file with JS (const { Uniwind } = require('uniwind'); Uniwind.__reinit(...)). It transforms that code as <file>.css.js and sets output[0].data.css. Metro keeps the module path global.css.
  • With optimize on, the Metro transform worker does not wrap modules. @expo/metro-config wraps them later, in build/serializer/reconcileTransformSerializerPlugin.js. That pass returns early for a path that matches /\.(s?css|sass)$/ (line 212 in 58.0.5, line 142 in 57.0.12). So the uniwind module is never wrapped, and its require is never rewritten.
  • Metro then appends ,<moduleId>,[deps] before the last ) of the code. In the output above, 1,[2] lands inside the __reinit(...) arguments.
  • When we removed the CSS check from that line in a local copy, the optimized export wrapped the module correctly and had no top-level require.

This follows #500. After that issue the code changed from import to require, which moved the failure from Hermes compilation to app launch. The fix may belong in uniwind (for example, a module path that does not end in .css) or in Expo's reconcile pass. We report it here first because uniwind produces JS for the CSS path.

Steps to Reproduce

  1. Clone the gist below: git clone https://gist.github.com/a27bd66ef154e9ea222473836c58c0b2.git uniwind-repro && cd uniwind-repro
  2. Run npm install --legacy-peer-deps. The flag is needed because uniwind's peer range does not match react-native@0.88.0-rc.1.
  3. Run EXPO_UNSTABLE_METRO_OPTIMIZE_GRAPH=1 EXPO_UNSTABLE_TREE_SHAKING=1 npx expo export --platform ios --no-bytecode --output-dir dist-opt.
  4. Run grep -c "^var{Uniwind:Uniwind}=require('uniwind')" dist-opt/_expo/static/js/ios/*.js. It prints 1.
  5. Run the same export without the two variables (--output-dir dist-plain). The same grep prints 0.
  6. In a Release build with the two variables set, the app crashes at launch with ReferenceError: Property 'require' doesn't exist.

Snack or Repository Link

https://gist.github.com/peterferguson/a27bd66ef154e9ea222473836c58c0b2

Uniwind version

1.12.0 (also reproduced on 1.6.4)

React Native Version

0.88.0-rc.1

Platforms

iOS (Android not tested)

Expo

Yes: expo 58.0.0-preview.4, @expo/metro-config 58.0.5 (the crash in our app is on 58.0.4)

Activity

  1. added
    bugSomething isn't working
    and removed
    invalidThis doesn't seem right
    on Sep 29, 2026
  2. self-assigned this
    on Sep 29, 2026
  3. github-actions commented on Oct 1, 2026

    @github-actions
    Contributor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions