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
- Create an Expo Router app from
create-better-t-stack with Uniwind in a Bun monorepo
- Keep a normal Expo Router / Expo dev client setup
- Use
withUniwindConfig(...) in metro.config.js
- Run a dev build / Android bundle (
expo run:android or expo export --platform android --clear)
- 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.
Description
withUniwindConfig(...)causes Metro to fail withInvariant Violation: Unexpectedly escaped traversalin an Expo Router app created fromcreate-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:
react-native-svg-transformerThe 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:
Metro fails during Android bundling / dev build startup with:
I also tested:
#353(react-native+isUniwindInternal(...)+metro-resolver.resolve(...)) -> did not fix it in this repoThe only workaround that fixed it here was directly resolving
uniwind/componentsanduniwind/components/*to Uniwind's native source files inmetro.config.js.Working workaround
This workaround consistently fixes the bundle in my repo:
Steps to Reproduce
create-better-t-stackwith Uniwind in a Bun monorepowithUniwindConfig(...)inmetro.config.jsexpo run:androidorexpo export --platform android --clear)Invariant Violation: Unexpectedly escaped traversalVersions
^1.6.1~55.0.17~55.0.130.83.619.2.01.3.11v24.14.1Additional notes
@sourcemonorepo issue from the Uniwind docs.#353were both insufficient here.uniwind/componentsfallback works, but it is too implementation-specific to feel like the right long-term solution.