Skip to content

Instantly share code, notes, and snippets.

@Zn4rK
Last active January 23, 2025 16:49
Show Gist options
  • Save Zn4rK/ed60c380e7b672e3089074f51792a2b8 to your computer and use it in GitHub Desktop.
Save Zn4rK/ed60c380e7b672e3089074f51792a2b8 to your computer and use it in GitHub Desktop.
pnpm and expo without node-linker=hoisted

I have a pretty big monorepo, and adding node-linker=hoisted to .npmrc would have significant consequences. With the help of rnx-kit, I managed to configure Expo to work without changes to .npmrc.

I haven't encountered any major issues yet, but I also haven't built or released a production version of my app, so we'll see how it goes.

Steps:

  1. Add the following dependencies to your package.json:

    {
      "@rnx-kit/metro-config": "^1.3.15",
      "@rnx-kit/metro-resolver-symlinks": "^0.1.36"
    }
  2. Update the main field in package.json to:

    {
      "main": "entry.js"
    }
  3. Add the files included in this gist.

  4. Perform a clean install of node_modules, and try running your app again.

  5. You might need to add EXPO_USE_METRO_WORKSPACE_ROOT=1 to your .env, or exporting it. Very unclear what it actually does.

Tip:

The SPA that react-native-web creates tends to be heavily cached, at least for me. Open the URL Expo provides in a private browser window for better results if you encounter any issues.

// Since I'm using expo-router, this is enough.
require('expo-router/entry');
const path = require("path");
const { FileStore } = require("metro-cache");
const { makeMetroConfig } = require("@rnx-kit/metro-config");
const { getDefaultConfig } = require("expo/metro-config");
const MetroSymlinksResolver = require("@rnx-kit/metro-resolver-symlinks");
const projectDir = __dirname;
const workspaceRoot = path.resolve(projectDir, "../..");
const symlinksResolver = MetroSymlinksResolver();
/** @type {import('expo/metro-config').MetroConfig} */
const expoConfig = getDefaultConfig(projectDir);
/** @type {import('expo/metro-config').MetroConfig} */
module.exports = makeMetroConfig({
...expoConfig,
resolver: {
...expoConfig.resolver,
resolveRequest: (context, moduleName, platform) => {
try {
// Symlinks resolver throws when it can't find what we're looking for.
const res = symlinksResolver(context, moduleName, platform);
if (res) {
return res;
}
} catch {
// If we have an error, we pass it on to the next resolver in the chain,
// which should be one of expos.
// https://github.com/expo/expo/blob/9c025ce7c10b23546ca889f3905f4a46d65608a4/packages/%40expo/cli/src/start/server/metro/withMetroResolvers.ts#L47
return context.resolveRequest(context, moduleName, platform);
}
},
},
watchFolders: [
workspaceRoot
],
cacheStores: [
new FileStore({
root: path.join(projectDir, "node_modules", ".cache", "metro"),
}),
]
});
@lbxa
Copy link

lbxa commented Oct 13, 2024

Using rnx-kit like this finally made pnpm doable with React Native. Thank you 🙏🏽

@Zn4rK
Copy link
Author

Zn4rK commented Oct 13, 2024

Np!

@midzdotdev
Copy link

Legend! No hoisting necessary 🙅‍♂️

@umsurething
Copy link

Brilliant. No hoisting! Let's goooooo!

@dhatGuy
Copy link

dhatGuy commented Dec 30, 2024

Nice, this works great.
Are you able to load local images? I keep getting ReactImageView: Image source "null" doesn't exist

@danieljvdm
Copy link

danieljvdm commented Jan 22, 2025

I copied this exactly and I get:
Error: Cannot find module '@react-native/metro-config'; as of React Native 0.72, it is required for configuring Metro correctly.

I resolved it by manually adding that package.

@danieljvdm
Copy link

My images with <Image source={require('some-local-path')} /> aren't resolving now

@jakozbek
Copy link

I also tried this and like @danieljvdm, had to manually install @react-native/metro-config. However, I still encountered the below error:

λ Bundled 3801ms node_modules/.pnpm/[email protected]_bc87ae3426177628a813014c240ac654/node_modules/expo-router/node/render.js (1053 modules)

Metro error: util.TextEncoder is not a constructor

TypeError: util.TextEncoder is not a constructor

@danieljvdm
Copy link

@jakozbek that seems like a node environment error. What version of node are you running?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment