Gaurab Paul

Polyglot software developer & consultant passionate about web development, distributed systems and open source technologies

Support my blog and open-source work

Tags

Using react-native (metro) in a pnpm workspace
Posted  10 days ago

For quite a while, metro and pnpm didn't play well with each other. Usually workarounds involved "shamefully-hoisting" which negated many benefits of pnpm.

However, recently metro has added support for symlinks (though marked unstable as of this writing) which makes it possible for us to use a react-native + metro project with pnpm.

There are a few considerations though:

  1. You may need to install some additional dev dependencies which didn't before.

    There are some dependencies which the RN project generated by the scaffolder uses, but doesn't depend on directly. They are just assumed to be available due to hoisting behavior of npm.

    pnpm adopts a more principled stance towards dependency hosting, which IMHO is better. So, you will need directly depend on these packages.

    pnpm i -D @react-native/gradle-plugin @react-native-community/cli-platform-android @react-native-community/cli

    Be mindful of versions if you are updating an older project that is not yet updated to use latest RN.

  2. Metro still needs to be aware of all source roots. This is an issue if we are also using pnpm workspaces.

    While the bundler can follow symlinks, we still need to explicitly register all the root directories which may contain bundled files (including the symlink targets) through the rather weirdly named watchFolders.

    From the official docs

    Despite the naming of this option, it isn't related solely to file watching. Even in an offline build (for example, in CI), all files must be visible to Metro through the combination of watchFolders and projectRoot.

    So let's say your RN project depends on another module in the pnpm workspace called shared, we need the following in metro.config.js:

    module.exports = mergeConfig(defaultConfig, {
        // ... other config
        watchFolders: [
            __dirname,
            path.resolve(__dirname, "../../node_modules"), // Should resolve to top level node_modules
            path.resolve(__dirname, "../shared"), // Any other modules the RN project depends on
        ],
    }

    As shown above, we also need to add the top level node_modules folder where pnpm will install the dependencies.

And that is pretty much all that is needed.