react-native-google-mobile-ads + expo + web

Version

Module (core, cmpapi, cli, stub, or testing)

core

Describe with reproduction steps – What is the expected behavior?

I’m using expo + web.
For the mobile ads I wantnd to use react-native-google-mobile-ads + conditional loading based on platform.

  if (Platform.OS !== 'web') {
    FFirstResultAdComponent = require("components/movies/result/first-result-ad-component").FirstResultAdComponent;
  }

The problem is that the library is still loaded by webpack for web build what leads to some issues while loading iabtcf and prevent page from loading.
What would be the best option to avoid this issues, exclude component + library from webpack? Or force to use cjs?

Json.js:74 Uncaught Error: Module parse failed: Unexpected token (7:16)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
|      * in that '+' and '/' are replaced with '-' and '_'.
|      */
>     static DICT = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
|     static REVERSE_DICT = new Map([
|         ['A', 0], ['B', 1], ['C', 2], ['D', 3], ['E', 4], ['F', 5],
    at ./node_modules/@iabtcf/core/lib/mjs/encoder/Base64Url.js (Json.js:74:1)
    at __webpack_require__ (bootstrap:789:1)
    at fn (bootstrap:100:1)
    at ./node_modules/@iabtcf/core/lib/mjs/encoder/index.js (index.js:1:1)
    at __webpack_require__ (bootstrap:789:1)
    at fn (bootstrap:100:1)
    at ./node_modules/@iabtcf/core/lib/mjs/index.js (index.js:1:1)
    at __webpack_require__ (bootstrap:789:1)
    at fn (bootstrap:100:1)
    at ./node_modules/react-native-google-mobile-ads/lib/module/AdsConsent.js (AdEventType.ts:18:1)
    at __webpack_require__ (bootstrap:789:1)
    at fn (bootstrap:100:1)
    at ./node_modules/react-native-google-mobile-ads/lib/module/index.js (index.ts:18:1)
    at __webpack_require__ (bootstrap:789:1)
    at fn (bootstrap:100:1)
    at ./App.tsx (bootstrap:856:1)
    at __webpack_require__ (bootstrap:789:1)
    at fn (bootstrap:100:1)
    at ./index.js (i18n.ts:10:1)
    at __webpack_require__ (bootstrap:789:1)
    at fn (bootstrap:100:1)
    at 1 (trpc-provider.tsx:35:1)
    at __webpack_require__ (bootstrap:789:1)
    at bootstrap:856:1
    at bootstrap:856:1
./node_modules/@iabtcf/core/lib/mjs/encoder/Base64Url.js @ Json.js:74
__webpack_require__ @ bootstrap:789
fn @ bootstrap:100
./node_modules/@iabtcf/core/lib/mjs/encoder/index.js @ index.js:1
__webpack_require__ @ bootstrap:789
fn @ bootstrap:100
./node_modules/@iabtcf/core/lib/mjs/index.js @ index.js:1
__webpack_require__ @ bootstrap:789
fn @ bootstrap:100
./node_modules/react-native-google-mobile-ads/lib/module/AdsConsent.js @ AdEventType.ts:18
__webpack_require__ @ bootstrap:789
fn @ bootstrap:100
./node_modules/react-native-google-mobile-ads/lib/module/index.js @ index.ts:18
__webpack_require__ @ bootstrap:789
fn @ bootstrap:100
./App.tsx @ bootstrap:856
__webpack_require__ @ bootstrap:789
fn @ bootstrap:100
./index.js @ i18n.ts:10
__webpack_require__ @ bootstrap:789
fn @ bootstrap:100
1 @ trpc-provider.tsx:35
__webpack_require__ @ bootstrap:789
(anonymous) @ bootstrap:856
(anonymous) @ bootstrap:856
VM1051:2 Uncaught ReferenceError: process is not defined
    at 4043 (<anonymous>:2:13168)
    at r (<anonymous>:2:306599)
    at 8048 (<anonymous>:2:9496)
    at r (<anonymous>:2:306599)
    at 8641 (<anonymous>:2:1379)
    at r (<anonymous>:2:306599)
    at <anonymous>:2:315627
    at <anonymous>:2:324225
    at <anonymous>:2:324229
    at e.onload (index.js:1:1)
4043 @ VM1051:2
r @ VM1051:2
8048 @ VM1051:2
r @ VM1051:2
8641 @ VM1051:2
r @ VM1051:2
(anonymous) @ VM1051:2
(anonymous) @ VM1051:2
(anonymous) @ VM1051:2
e.onload @ index.js:1
be @ index.js:1
de @ index.js:1
handleErrors @ webpackHotDevClient.js:169
./node_modules/react-dev-utils/webpackHotDevClient.js.connection.onmessage @ webpackHotDevClient.js:213
load (async)
be @ index.js:1
de @ index.js:1
handleErrors @ webpackHotDevClient.js:169
./node_modules/react-dev-utils/webpackHotDevClient.js.connection.onmessage @ webpackHotDevClient.js:213
index.js:1 ./node_modules/@iabtcf/core/lib/mjs/model/PurposeRestrictionVector.js 10:14
Module parse failed: Unexpected token (10:14)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
|      * bit length; it can be set and got from here
|      */
>     bitLength = 0;
|     /**
|      * a map indexed by a string which will be a 'hash' of the purpose and

webpack.config.js

const createExpoWebpackConfigAsync = require('@expo/webpack-config');
const path = require('path')

module.exports = async function (env, argv) {
    const config = await createExpoWebpackConfigAsync({
            ...env,
            babel: {
                dangerouslyAddModulePathsToTranspile: ["@trpc/react-query",
                    "@trpc/client", "@tanstack/react-query"],
            },
        },
        argv
    );
    config.module.rules.push(
        {
            test: /.mjs$/,
            include: /node_modules/,
            use: {
                loader: 'babel-loader',
                options: {
                    presets: [
                        [
                            "@babel/preset-env",
                            {
                                loose: false,
                            }
                        ]
                    ],
                    plugins: ['@babel/plugin-proposal-class-properties',
                        "@babel/plugin-proposal-private-property-in-object",
                        "@babel/plugin-proposal-optional-chaining"]
                }
            }
        });

    config.module.rules.push({
        test: /\.mjs$/,
        include: /node_modules/,
        type: "javascript/auto"
    });

    return config;
};

Platforms

Android and iOS

Are your using Typescript?

  • My project is using Typescript

package.json

{
  "version": "1.0.0",
  "scripts": {
    "start": "expo start --dev-client",
  },
  "dependencies": {
    "@babel/plugin-proposal-export-namespace-from": "^7.18.9",
    "@babel/plugin-proposal-optional-chaining": "^7.18.9",
    "@babel/plugin-proposal-private-methods": "^7.18.6",
    "@babel/preset-env": "^7.20.2",
    "@expo/html-elements": "^0.2.2",
    "@expo/vector-icons": "^13.0.0",
    "@expo/webpack-config": "^0.17.3",
    "@hapi/iron": "^7.0.0",
    "@iabtcf/core": "^1.5.5",
    "@prisma/client": "^4.6.1",
    "@react-navigation/bottom-tabs": "^6.4.3",
    "@react-navigation/native": "^6.0.14",
    "@react-navigation/native-stack": "^6.9.2",
    "@react-navigation/stack": "^6.3.5",
    "@rneui/base": "^4.0.0-rc.7",
    "@rneui/themed": "^4.0.0-rc.7",
    "@tanstack/react-query": "^4.3.8",
    "@trpc/client": "^10.4.2",
    "@trpc/react-query": "^10.4.2",
    "@trpc/server": "^10.4.2",
    "@types/react": "~18.0.24",
    "@types/react-native": "~0.70.6",
    "babel-loader": "^8.3.0",
    "babel-plugin-jest-hoist": "^29.2.0",
    "babel-preset-env": "^1.7.0",
    "babel-preset-expo": "^9.2.2",
    "cookie": "^0.5.0",
    "cors": "^2.8.5",
    "expo": "~47.0.8",
    "expo-asset": "~8.6.2",
    "expo-auth-session": "~3.7.3",
    "expo-blur": "~12.0.1",
    "expo-build-properties": "~0.4.1",
    "expo-dev-client": "~2.0.1",
    "expo-localization": "~14.0.0",
    "expo-random": "~13.0.0",
    "expo-secure-store": "~12.0.0",
    "expo-splash-screen": "~0.17.5",
    "expo-status-bar": "~1.4.2",
    "expo-updates": "~0.15.6",
    "i18n-js": "^4.2.0",
    "install": "^0.13.0",
    "iron-session": "^6.3.1",
    "next": "^13.0.4",
    "next-compose-plugins": "^2.2.1",
    "next-connect": "^0.13.0",
    "next-fonts": "^1.5.1",
    "next-images": "^1.8.4",
    "next-transpile-modules": "^10.0.0",
    "nextjs-cors": "^2.1.2",
    "npm": "^9.1.3",
    "react": "18.1.0",
    "react-dev-tools": "^0.0.1",
    "react-dom": "18.1.0",
    "react-native": "0.70.5",
    "react-native-elements": "^4.0.0-beta.0",
    "react-native-fbsdk-next": "^11.1.0",
    "react-native-gesture-handler": "~2.8.0",
    "react-native-google-mobile-ads": "^8.2.2",
    "react-native-reanimated": "~2.12.0",
    "react-native-safe-area-context": "^4.4.1",
    "react-native-screens": "~3.18.0",
    "react-native-vector-icons": "^9.2.0",
    "react-native-web": "~0.18.9",
    "rimraf": "^3.0.2",
    "typescript": "^4.6.3",
    "zod": "^3.19.1"
  },
  "devDependencies": {
    "@babel/core": "^7.12.9",
    "@expo/next-adapter": "^4.0.13",
    "babel-plugin-module-resolver": "^4.1.0",
    "prisma": "^4.6.1"
  },
  "prisma": {
    "schema": "schema.prisma",
    "seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts"
  },
  "private": true
}

I think I have faced the same situation as you. I target the mobile platform, yet I like the speedy development on browsers, but that library just cannot run for the web platform.

My poor solution is to create two files for the same react component, named AdCom.tsx.webDev / AdCom.tsx.mobDev for example. Depending on the situation, I switch between these files by renaming the targeted one to AdCom.tsx.

Hi @ericcatexpo
the correct solution is simple like a harm, just create wrappers for your components and libraries.
the first one with .tsx file extensions, that will be used for mobiles and only this one should point to react-native-google-mobile-ads
and second one .web.tsx that could be completely blank, the only important thing is that he cannot have any references to react-native-google-mobile-ads
and then webpack will do the job.

2 Likes

Hi @ericcatexpo

As @dezerb implies, there is built-in support for this sort of thing.

So you can e.g. create a file called AdCom.tsx and another one called AdCom.web.tsx. Then AdCom.tsx will be used for iOS or Android, but AdCom.web.tsx will be used for web.

2 Likes

Hi @dezerb and @wodin

Thanks for the suggestion! It will definitely improve my own project too. Happy holiday season!

1 Like