Skip to content

Instantly share code, notes, and snippets.

@Zn4rK
Last active July 30, 2024 07:32
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"),
}),
]
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment