Skip to content

Expo dev-client + Bun monorepo: Metro 'Unexpectedly escaped traversal' with withUniwindConfig #512

@AZagatti

Description

@AZagatti

Description

withUniwindConfig(...) causes Metro to fail with Invariant Violation: Unexpectedly escaped traversal in an Expo Router app created from create-better-t-stack, using Bun workspaces in a monorepo and running a dev build (expo-dev-client).

This happens in a relatively standard Expo setup:

  • Expo Router
  • Expo dev client
  • Bun monorepo
  • no module federation
  • no unusual Metro plugins beyond react-native-svg-transformer

The app bundles and runs once I add a local Metro workaround, but the stock Uniwind setup crashes Metro.

What happened?

With the default Uniwind quickstart setup:

const { getDefaultConfig } = require('expo/metro-config');
const { withUniwindConfig } = require('uniwind/metro');

const config = getDefaultConfig(__dirname);
module.exports = withUniwindConfig(config, {
  cssEntryFile: './global.css',
  dtsFile: './uniwind-types.d.ts',
});

Metro fails during Android bundling / dev build startup with:

Invariant Violation: Unexpectedly escaped traversal
    at invariant (.../node_modules/invariant/invariant.js:40:15)
    at TreeFS.#lookupByNormalPath (.../node_modules/metro-file-map/src/lib/TreeFS.js:536:28)
    at TreeFS.hierarchicalLookup (.../node_modules/metro-file-map/src/lib/TreeFS.js:552:51)
    ...
    at nativeResolver (.../node_modules/uniwind/dist/metro/index.cjs:81:34)

I also tested:

  • Uniwind FAQ/package-exports style resolver tweaks -> did not fix it
  • the narrower workaround from issue #353 (react-native + isUniwindInternal(...) + metro-resolver.resolve(...)) -> did not fix it in this repo

The only workaround that fixed it here was directly resolving uniwind/components and uniwind/components/* to Uniwind's native source files in metro.config.js.

Working workaround

This workaround consistently fixes the bundle in my repo:

const { getDefaultConfig } = require('expo/metro-config')
const path = require('node:path')
const { withUniwindConfig } = require('uniwind/metro')

const projectRoot = __dirname
const workspaceRoot = path.resolve(projectRoot, '../..')
const config = getDefaultConfig(projectRoot)

config.resolver = {
  ...config.resolver,
  resolveRequest: (context, moduleName, platform) => {
    if (platform !== 'web' && moduleName === 'uniwind/components') {
      return {
        type: 'sourceFile',
        filePath: path.resolve(workspaceRoot, 'node_modules/uniwind/src/components/index.ts'),
      }
    }

    if (platform !== 'web' && moduleName.startsWith('uniwind/components/')) {
      const componentName = moduleName.slice('uniwind/components/'.length)

      return {
        type: 'sourceFile',
        filePath: path.resolve(
          workspaceRoot,
          `node_modules/uniwind/src/components/native/${componentName}.tsx`
        ),
      }
    }

    return context.resolveRequest(context, moduleName, platform)
  },
}

module.exports = withUniwindConfig(config, {
  cssEntryFile: './global.css',
  dtsFile: './uniwind-types.d.ts',
})

Steps to Reproduce

  1. Create an Expo Router app from create-better-t-stack with Uniwind in a Bun monorepo
  2. Keep a normal Expo Router / Expo dev client setup
  3. Use withUniwindConfig(...) in metro.config.js
  4. Run a dev build / Android bundle (expo run:android or expo export --platform android --clear)
  5. Metro crashes with Invariant Violation: Unexpectedly escaped traversal

Versions

  • Uniwind: ^1.6.1
  • Expo: ~55.0.17
  • Expo Router: ~55.0.13
  • React Native: 0.83.6
  • React: 19.2.0
  • Bun: 1.3.11
  • Node: v24.14.1
  • Platform tested: Android / Expo dev client

Additional notes

  • This is not just a CSS scanning / @source monorepo issue from the Uniwind docs.
  • It appears to be a Metro resolver interaction specific to this setup.
  • The package-exports FAQ workaround and the fix discussed in #353 were both insufficient here.
  • The direct uniwind/components fallback works, but it is too implementation-specific to feel like the right long-term solution.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions