Skip to main content

Bundler Polyfill Issues [MPC] - React Native Metro

While setting up a new web3 project with MPC Core Kit from scratch, you might face multiple issues with the bundler. This issue is caused because the core packages like eccrypto, tsslib have certain dependencies, which are not present within the build environment. For rectifying this, the go-to method has been to just add the missing modules directly into the package, and edit the bundler configuration to take advantage of that.

Although this method works, it increases the bundle size significantly. Causing slow loading speeds and a bad user experience. It is important to note that these modules, even while the build fails, are still present within the browser environment. Many libraries like Web3Auth are written in a way to take advantage of this fact. Hence, even if the build doesn't contain the polyfill, the library should still work as expected. However, if you are using a library that does not take advantage of this fact, you might face issues while using the library.

Hence, you need to be mindful of the fact that you only require certain node polyfills to be added to your project, while testing each of its functionalities. At the same time, you need to tell the bundler to ignore the missing modules, and not include them in the build.

In this guide, we have added instructions for adding the polyfills in React Native Metro.

Install the missing modules

Check for the missing libraries in your build and included packages, and accordingly polyfill them. For Web3Auth, you need to polyfill the buffer, process, crypto and stream libraries. For the rest of the libraries, we are installing a dummy module called empty-module which helps us get rid of the warnings while building the project.

npm install --save empty-module readable-stream crypto-browserify react-native-get-random-values buffer process react-native-url-polyfill
warning

There might be a possibility that you might need to polyfill more libraries, in case you're using any other blockchain library alongside Web3Auth that requires them. Generally, the libraries like browserify-zlib, assert, stream-http, https-browserify, os-browserify, url are the ones that might be required.

Update your metro.config.js

To make use of the polyfilled modules while building the application, you need to reconfigure your metro bundler config.

You can copy the following code in your metro.config.js file. This will tell the bundler to ignore the missing modules and include the ones that are needed.

metro.config.js

metro.config.js
const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config");

const config = {
resolver: {
extraNodeModules: {
assert: require.resolve("empty-module"), // assert can be polyfilled here if needed
http: require.resolve("empty-module"), // stream-http can be polyfilled here if needed
https: require.resolve("empty-module"), // https-browserify can be polyfilled here if needed
os: require.resolve("empty-module"), // os-browserify can be polyfilled here if needed
url: require.resolve("empty-module"), // url can be polyfilled here if needed
zlib: require.resolve("empty-module"), // browserify-zlib can be polyfilled here if needed
path: require.resolve("empty-module"),
crypto: require.resolve("crypto-browserify"),
stream: require.resolve("stream-browserify"),
buffer: require.resolve("buffer"),
},

// assetExts: assetExts.filter(ext => ext !== 'svg'),

assetExts: ["svg", "png", "json"],
sourceExts: ["js", "mjs", "cjs", "jsx", "ts", "tsx"],
// sourceExts: process.env.TEST_REACT_NATIVE
// ? ['e2e.js'].concat(defaultSourceExts)
// : defaultSourceExts,
},
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: true,
inlineRequires: true,
},
}),
// This detects entry points of React app and transforms them
// For the other files this will switch to use default `metro-react-native-babel-transformer` for transforming
babelTransformerPath: require.resolve("react-native-react-bridge/lib/plugin"),
},
};

module.exports = mergeConfig(getDefaultConfig(__dirname), config);
// })();

Fixing additional dependency issues

  • Create a globals.js to your project root directory and add the following code to it.
globals.js
// global.Buffer = require('buffer').Buffer;
if (typeof __dirname === "undefined") global.__dirname = "/";
if (typeof __filename === "undefined") global.__filename = "";
if (typeof process === "undefined") {
global.process = require("process");
} else {
const bProcess = require("process");
for (var p in bProcess) {
if (!(p in process)) {
process[p] = bProcess[p];
}
}
}

process.browser = false;
if (typeof Buffer === "undefined") global.Buffer = require("buffer").Buffer;

// global.location = global.location || { port: 80 }
const isDev = typeof __DEV__ === "boolean" && __DEV__;
process.env["NODE_ENV"] = isDev ? "development" : "production";
if (typeof localStorage !== "undefined") {
localStorage.debug = isDev ? "*" : "";
}

// If using the crypto shim, uncomment the following line to ensure
// crypto is loaded first, so it can populate global.crypto
// require('crypto')

console.log(Buffer.from("Hello World!", "utf-8").toString("base64"));
// Needed so that 'stream-http' chooses the right default protocol.
global.location = {
protocol: "file:",
};

global.process.version = "v16.0.0";
if (!global.process.version) {
global.process = require("process");
console.log({ process: global.process });
}

process.browser = true;

const TextEncodingPolyfill = require("text-encoding");
// const WebAssembly = require('react-native-webassembly');

// const BigInt = require('big-integer')
Object.assign(global, {
TextEncoder: TextEncodingPolyfill.TextEncoder,
TextDecoder: TextEncodingPolyfill.TextDecoder,
// WebAssembly: WebAssembly,
// BigInt: BigInt,
});
  • Then, import the dependencies to index.js of your project.
index.js
import { AppRegistry } from "react-native";
import "./globals";
import "react-native-get-random-values";
import { name as appName } from "./app.json";
import "react-native-url-polyfill/auto";

import App from "./App";

AppRegistry.registerComponent(appName, () => App);