react-native-maps: Unable to resolve "../Utilities/Platform" w/ Metro bundler

Summary

Using the new Metro Bundler and, in my case, react-native-maps, I have compatibilities issues when building for web.

I have some code requiring react-native-maps like so:

if (Platform != 'web') {
  const MapView = require(`react-native-maps`).default
  // ...
}

When building for native everything works fine, but during a web build even though this code is not executed at runtime it seems like Metro is still trying to resolve react-native-maps and by doing so the compilation is broken, I’ll have errors like these:

Unable to resolve "../Utilities/Platform" from "node_modules/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js"

(because Platform.web.js does not exists)

However if I require react-native-maps “obfucasting” the path for Metro not to track the module like so:

export function requireNativeOnly<T = any>(path: string): T {
  return ({
    'require': require,
  })['require'](path)
}
// ...
if (Platform != 'web') {
  const MapView = requireNativeOnly(`react-native-maps`).default
  // ...
}

My web build will work perfectly fine but on native at runtime when this module is required (through requireNativeOnly) I’ll have errors saying this module cannot be found:

Unknown named module: "react-native-maps"

Reproducible sample code

if (Platform != 'web') {
  const MapView = require(`react-native-maps`).default
  // ...
}

Steps to reproduce

Try to make a web build using metro and the code above.

expo start --clear --web

Expected result

I would expect require(react-native-maps) not to be executed/resolved on a web build. OR I would expect require(react-native-maps) to be resolved to an empty module on a web build.

Actual result

Metro tells me:

Web Bundling failed 12368ms
Unable to resolve "../Utilities/Platform" from "node_modules/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js"

React Native Maps Version

1.3.2

What platforms are you seeing the problem on?

Android, iOS (Apple Maps), iOS (Google Maps)

React Native Version

0.71.3

What version of Expo are you using?

SDK 48

Device(s)

Web

Additional information

No response

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 8
  • Comments: 25

Commits related to this issue

Most upvoted comments

@monholm Why was this closed? From the comments it’s pretty clear that people are having problems with this. (Yes, I’m one of them, that’s how I got here…)

I tried to use this script above, but nothing changed, i still got the same issue.

For what it’s worth, I got this to work by adding a file called postinstall.js in my repository with this content:

const chalk = require('chalk')
const { readFile, writeFile, copyFile } = require('fs').promises

console.log(chalk.green('here'))

function log(...args) {
  console.log(chalk.yellow('[react-native-maps]'), ...args)
}

reactNativeMaps = async function() {
  log('📦 Creating web compatibility of react-native-maps using an empty module loaded on web builds')
  const modulePath = 'node_modules/react-native-maps'
  await writeFile(`${modulePath}/lib/index.web.js`, 'module.exports = {}', 'utf-8')
  await copyFile(`${modulePath}/lib/index.d.ts`, `${modulePath}/lib/index.web.d.ts`)
  const pkg = JSON.parse(await readFile(`${modulePath}/package.json`))
  pkg['react-native'] = 'lib/index.js'
  pkg['main'] = 'lib/index.web.js'
  await writeFile(`${modulePath}/package.json`, JSON.stringify(pkg, null, 2), 'utf-8')
  log('✅ script ran successfully')
}

reactNativeMaps()

, adding a script in package.lock:

  "scripts": {
   ....
    "postinstall": "node postinstall.js",
   ....
  },

and then running npm install created the correct files. I’m still not able to conditionally render react native maps. For some reason, when running on web, even wrapped in a check for the platform os, the component using react native maps still gets loaded. Maybe I’m doing something wrong though.

no attention… let’s make a new issue?

On Tue, 12 Mar 2024 at 15:37, Camryn @.***> wrote:

Is there any solution that works for now? Will they even fix this issue? Currently running the same problem.

— Reply to this email directly, view it on GitHub https://github.com/react-native-maps/react-native-maps/issues/4641#issuecomment-1991802382, or unsubscribe https://github.com/notifications/unsubscribe-auth/ANHYNGTMOIK6HIGYJK5Z67LYX4HKXAVCNFSM6AAAAAAV6OHUEKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSOJRHAYDEMZYGI . You are receiving this because you commented.Message ID: @.***>

Kudos to @vonkanehoffen for the sound solution.

Expo Router docs seem to suggest this approach as well:

https://docs.expo.dev/router/advanced/platform-specific-modules/#platform-specific-extensions

A sound solution is hardly one that disables compatibility for web (which his solution replaces the web MapView with blank div elements.) We need a working solution that enables cross-platform compatibility for this particular component.

I am still getting the same problem.

This should be re-opened and addressed.

Bump +1

I just got this coming from the react-native module itself.:

Web Bundling failed 8511ms (node_modules/expo/AppEntry.js)
Unable to resolve "../../Utilities/Platform" from "node_modules/react-native/Libraries/NewAppScreen/components/DebugInstructions.js"

Kudos to @vonkanehoffen for the sound solution. Expo Router docs seem to suggest this approach as well: https://docs.expo.dev/router/advanced/platform-specific-modules/#platform-specific-extensions

A sound solution is hardly one that disables compatibility for web (which his solution replaces the web MapView with blank div elements.) We need a working solution that enables cross-platform compatibility for this particular component.

+1

Kudos to @vonkanehoffen for the sound solution.

Expo Router docs seem to suggest this approach as well:

https://docs.expo.dev/router/advanced/platform-specific-modules/#platform-specific-extensions

Props to you for that solution @cavewebs but altering node_modules scares me a bit 😉

My solution for this is to create a module with dummy components (I only need this for testing). So:

MapView.ts

import { MapMarkerProps, MapPolylineProps, MapViewProps } from 'react-native-maps';

const MapView = (props: MapViewProps) => <div />;
const Marker = (props: MapMarkerProps) => <div />;
const Polyline = (props: MapPolylineProps) => <div />;
const PROVIDER_GOOGLE = 'google';

export default MapView;
export { Marker, Polyline, PROVIDER_GOOGLE };

and so it still works on iOS and Android, add a native only version:

MapView.native.ts

import MapView, { Marker, Polyline, PROVIDER_GOOGLE } from 'react-native-maps';

export default MapView;
export { Marker, Polyline, PROVIDER_GOOGLE };

You could even write non-native components that share the same API so this works on web I guess.

These can then be imported as normal and Metro will choose the appropriate one:

import MapView, { Marker, Polyline, PROVIDER_GOOGLE } from '@/components/MapView';