client-sdk-react-native icon indicating copy to clipboard operation
client-sdk-react-native copied to clipboard

Unable to resolve "event-target-shim/index" from "node_modules/@livekit/react-native-webrtc/src/MediaDevices.ts"

Open sharad-incapsulate opened this issue 11 months ago • 14 comments

Describe the bug After installing all dependencies as per document we are facing "Unable to resolve "event-target-shim/index" from "node_modules/@livekit/react-native-webrtc/src/MediaDevices.ts" error.

To Reproduce

Steps to reproduce the behavior:

  1. Install all dependencies as per document.
  2. Run npx expo run:ios
  3. See error

Expected behavior

App should work without any issues

Screenshots

Image

Device Info:

  • Device: Iphone 16
  • OS: 18

Dependencies Info (please reference your package-lock.json or yarn.lock file, not just your package.json):

  • @livekit/react-native:
  • livekit-client:
  • react-native-webrtc:

Additional context We are having bare managed flow for react native expo.

sharad-incapsulate avatar Mar 26 '25 13:03 sharad-incapsulate

Do you have a metro.config.js file in your expo project? If so, you may have changed your resolver in a way that it can't find the event-target-shim package. You might need to add a new config for config.resolver.extraNodeModules that points it to the correct path.

davidliu avatar Apr 07 '25 08:04 davidliu

When we try to make import like

import { EventTarget, Event, defineEventAttribute } from 'event-target-shim'

instead of

import { EventTarget, Event, defineEventAttribute } from 'event-target-shim/index' 

updated this changes in node_modules for time being. WIth this the above issue is resolved but now getting following error on import

import {registerGlobals} from '@livekit/react-native';
registerGlobals();

ERROR TypeError: Super expression must either be null or a function, js engine: hermes

sharad-incapsulate avatar Apr 08 '25 10:04 sharad-incapsulate

Yeah, that won't work, since the event-target-shim and event-target-shim/index are meant to resolve to two separate packages, due to RN and RN-webrtc resolving to two separate versions of the package. Context here: https://github.com/react-native-webrtc/react-native-webrtc/issues/1503

Can you provide your metro.config.js file?

davidliu avatar Apr 08 '25 17:04 davidliu

Here is my config file

const {getDefaultConfig} = require('expo/metro-config'); const path = require('path'); const fs = require('fs');

// Set your root paths const projectRoot = __dirname; const workspaceRoot = path.resolve(projectRoot, '../..'); const packagesRoot = path.resolve(workspaceRoot, 'packages'); const coreAppRoot = path.resolve(packagesRoot, 'core');

// Get default config and override projectRoot const config = getDefaultConfig(projectRoot); config.projectRoot = coreAppRoot;

// Optional: Add source extensions if needed config.resolver.sourceExts = [...config.resolver.sourceExts, 'cjs', 'mjs'];

// Add terser minifier config.transformer.minifierPath = 'metro-minify-terser';

// 1. Watch entire monorepo config.watchFolders = [workspaceRoot];

// 2. Set node module resolution paths const getDirs = (root) => fs .readdirSync(root, {withFileTypes: true}) .filter((dir) => dir.isDirectory()) .filter((dir) => dir.name !== 'core') // skip core since it's already set as projectRoot .map((dir) => dir.name);

const packages = getDirs(packagesRoot);

config.resolver.nodeModulesPaths = [ path.resolve(projectRoot, 'node_modules'), path.resolve(workspaceRoot, 'node_modules'), ...packages.map((packageName) => path.resolve(packagesRoot, packageName, 'node_modules'), ), ];

// 3. Force Metro to resolve subdependencies only from above paths config.resolver.disableHierarchicalLookup = true;

// 4. Extra polyfills or shims config.resolver.extraNodeModules = { stream: require.resolve('readable-stream'), 'event-target-shim/index': path.resolve( workspaceRoot, 'node_modules/event-target-shim/index', ), };

// Optional: Rewrite URL workaround for iOS config.server.rewriteRequestUrl = (url) => { if (!url.endsWith('.bundle')) return url;

return ( url + '?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true' ); };

module.exports = config;

sharad-incapsulate avatar Apr 09 '25 04:04 sharad-incapsulate

Hi @davidliu Can you please provide any update on above issue ?

sharad-incapsulate avatar Apr 11 '25 05:04 sharad-incapsulate

I think the config here is disabling it from looking up subdependencies:

// 3. Force Metro to resolve subdependencies only from above paths
config.resolver.disableHierarchicalLookup = true;

See this for reference: https://github.com/react-native-webrtc/react-native-webrtc/issues/1567#issuecomment-2227736860

If it's possible to turn it to false, that probably would fix it directly.

Alternatively, if that's not possible, then you can go look for where the event-target-shim subdependency gets put and add it to config.resolver.extraNodeModules or config.resolver.nodeModulesPaths. Note that react-native depends on ~5.0.0, but react-native-webrtc depends on ~6.0.0. Verify the package you're pointing at is the ~6.0.0 version. I think the existing one you're pointing at might be the ~5.0.0 version.

For reference default expo with yarn or npm installs it at the path node_modules/@livekit/react-native-webrtc/node_modules/event-target-shim for me, but this may be different for you.

If this doesn't work, I'll need some details on how your workspace is setup so I can emulate it myself, including the package manager and version that you're using.

davidliu avatar Apr 13 '25 16:04 davidliu

@davidliu Above issue resolved after adding following in metro config, but facing new error after this.

config.resolver.resolveRequest = (context, moduleName, platform) => {
  if (
    // If the bundle is resolving "event-target-shim" from a module that is part of "react-native-webrtc".
    // moduleName.startsWith('event-target-shim') &&¸
    context.originModulePath.includes('react-native-webrtc')
  ) {
    // Resolve event-target-shim relative to the react-native-webrtc package to use v6.
    // React Native requires v5 which is not compatible with react-native-webrtc.
    const eventTargetShimPath = resolveFrom(
      context.originModulePath,
      moduleName,
    );

    return {
      filePath: eventTargetShimPath,
      type: 'sourceFile',
    };
  }

  // Ensure you call the default resolver.
  return context.resolveRequest(context, moduleName, platform);
};

ERROR Error: Cannot find module './EventEmitter' Require stack:

  • /Users/xyz/Documents/xxxx/node_modules/@livekit/react-native-webrtc/src/index.ts/noop.js

sharad-incapsulate avatar Apr 16 '25 04:04 sharad-incapsulate

What's the purpose of the rest of the stuff in the metro config? This would be faster if I could emulate the setup and package manager you're using to repro, but it seems very custom.

davidliu avatar Apr 16 '25 04:04 davidliu

@davidliu The rest stuff is related to other features in the project, i have tested after removing those from metro config still i face that issue.

iOS packages/core/index.js ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ 95.2% (4254/4359)[metropolis311.debug.dylib] Metro has encountered an error: Package subpath './index' is not defined by "exports" in
/Users/sharad.pawar/Documents/xyz-mobile/node_modules/@livekit/react-native-webrtc/node_modules/event-target-shim/package.json: node:internal/errors (405:5)
 ERROR  Error: Package subpath './index' is not defined by "exports" in /Users/sharad.pawar/Documents/xyz-mobile/node_modules/@livekit/react-native-webrtc/node_modules/event-target-shim/package.json

Below is current metro config

const {getDefaultConfig} = require('expo/metro-config');
const path = require('path');
const fs = require('fs');
const resolveFrom = require('resolve-from');

// Set your root paths
const projectRoot = __dirname;
const workspaceRoot = path.resolve(projectRoot, '../..');
const packagesRoot = path.resolve(workspaceRoot, 'packages');
const coreAppRoot = path.resolve(packagesRoot, 'core');

// // Get default config and override projectRoot
const config = getDefaultConfig(projectRoot);
config.projectRoot = coreAppRoot;

// // Optional: Add source extensions if needed
config.resolver.sourceExts = [...config.resolver.sourceExts, 'cjs', 'mjs'];

// // Add terser minifier
config.transformer.minifierPath = 'metro-minify-terser';

// // 1. Watch entire monorepo
config.watchFolders = [workspaceRoot];

// 2. Set node module resolution paths
const getDirs = (root) =>
  fs
    .readdirSync(root, {withFileTypes: true})
    .filter((dir) => dir.isDirectory())
    .filter((dir) => dir.name !== 'core') // skip core since it's already set as projectRoot
    .map((dir) => dir.name);

const packages = getDirs(packagesRoot);

config.resolver.nodeModulesPaths = [
  path.resolve(projectRoot, 'node_modules'),
  path.resolve(workspaceRoot, 'node_modules'),
  ...packages.map((packageName) =>
    path.resolve(packagesRoot, packageName, 'node_modules'),
  ),
];


// Optional: Rewrite URL workaround for iOS
config.server.rewriteRequestUrl = (url) => {
  if (!url.endsWith('.bundle')) return url;

  return (
    url + '?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true'
  );
};

// Learn more https://docs.expo.io/guides/customizing-metro

/** @type {import('expo/metro-config').MetroConfig} */

Do you still think it is something with metro config?

sharad-incapsulate avatar Apr 17 '25 08:04 sharad-incapsulate

We're encountering the same issue:

iOS Bundling failed 42470ms index.js (13723 modules)
error: Error: Missing "./index" specifier in "event-target-shim" package

Here is our metro.config.js. It's been modified to support our monorepo structure and Package Exports:

const { getDefaultConfig } = require('expo/metro-config');
const path = require('path');

const projectRoot = __dirname;
const monorepoRoot = path.resolve(projectRoot, '..');

/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);

// https://reactnative.dev/blog/2023/06/21/package-exports-support
config.resolver.unstable_enablePackageExports = true;

config.transformer.getTransformOptions = async () => ({
  transform: {
    inlineRequires: true,
  },
});

// Watch monorepo root node_modules for changes
config.watchFolders = [
  path.resolve(monorepoRoot, 'node_modules'),
  path.resolve(monorepoRoot, 'shared'),
  projectRoot,
];

config.resolver.nodeModulesPaths = [
  path.resolve(projectRoot, 'node_modules'),
  path.resolve(monorepoRoot, 'node_modules'),
];

module.exports = config;

We're using pnpm 10.7.0 with node-linker=hoisted (as required for Expo), Expo 51.0.30, and React Native 0.74.5.

Our structure looks something like this:

.
|- package.json
|- node_modules/
|- app/
   |- node_modules/
   |- package.json
   |- ...
|- web/
   |- node_modules/
   |- package.json
   |- ...
|- shared/
   |- ...

where app/ has the RN/Expo LiveKit SDK packages installed, and web/ has the web/React LiveKit SDK packages.

I tried the modified config.resolver.resolveRequest @shared-incapsulate shared above and also ran into the same error as @sharad-incapsulate.

net avatar Apr 23 '25 22:04 net

I resolved this by patching @livekit/react-native-webrtc to replace all instances of event-target-shim/index with event-target-shim.

I did not get an error on registerGlobals();, so it would seem something about how we're resolving modules resolves them correctly. Perhaps having

config.resolver.unstable_enablePackageExports = true;

fixes the issue with disambiguating versions.

net avatar Apr 24 '25 18:04 net

I was porting the sample https://github.com/livekit-examples/react-native-meet to test out in the latest expo and I get this similar error

 WARN  Attempted to import the module "/.../expo-audio-app-test-livekit/test/node_modules/@livekit/react-native-webrtc/node_modules/event-target-shim/index" which is not listed in the "exports" of "/.../expo-audio-app-test-livekit/test/node_modules/@livekit/react-native-webrtc/node_modules/event-target-shim" under the requested subpath "./index". Falling back to file-based resolution. Consider updating the call site or asking the package maintainer(s) to expose this API.
iOS Bundling failed 8716ms node_modules/expo-router/entry.js (1327 modules)
 ERROR  SyntaxError: /.../expo-audio-app-test-livekit/test/app/src/callservice/CallService.d.ts: Missing semicolon. (1:7)

> 1 | declare async function startCallService();
    |        ^
  2 | declare async function stopCallService();
  3 | export { startCallService, stopCallService };

which seems to be similar error. Any help here?

All i did was create a sample default expo app then imported the <App /> component (and its deps) the react-native-meet repo

dchhetri avatar Jul 07 '25 02:07 dchhetri

It would appear this is a bug with @livekit/react-native-webrtc.

I'll start with my workaround, then explain why I think it's happening.

Workaround

First, you'll want to ensure every dependency uses the version of `event-target-shim` that supports the `exports` field.

For pnpm:

// in the root package.json:

  "resolutions": {
    "event-target-shim": "6.0.2"
  },

Then, you can apply this minimal patch for [email protected] to add /index as an export path and point it to the entrypoint:

diff --git a/package.json b/package.json
index 045cf8bb2890a73e03d8aad727ab9f81bae2176a..420303ed1a1f19884d53f8d6bc23316cb68a3973 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,10 @@
       "import": "./index.mjs",
       "require": "./index.js"
     },
+    "./index": {
+      "import": "./index.mjs",
+      "require": "./index.js"
+    },
     "./es5": {
       "import": "./es5.mjs",
       "require": "./es5.js"

This will ensure @livekit/react-native-webrtc can resolve the bad imports until a proper fix is available.

Root Cause

Every file depending on `event-target-shim` imports it like so:
import { EventTarget, defineEventAttribute } from 'event-target-shim/index';

Module resolution is pretty gnarly for TS projects, so there are a couple of reasons this might have been valid at once (or seemed valid and passed CI, but then breaks in other projects).

Taking a look at version 5 of event-target-shim, I noticed it relies on the legacy main field in package.json:

  "main": "dist/event-target-shim",
  "types": "index.d.ts",
  "files": [
    "dist",
    "index.d.ts"
  ]

The correct import specifier in this case would be event-target-shim (without the /index supbath).

Version 6, however, embraces the modern best practice of using the exports in package.json (but retains the main field as a fallback):

 "main": "index.js",
  "exports": {
    ".": {
      "import": "./index.mjs",
      "require": "./index.js"
    },
    "./es5": {
      "import": "./es5.mjs",
      "require": "./es5.js"
    },
    "./umd": "./umd.js",
    "./package.json": "./package.json"
  },

There's no ./index under the exports field—so the correct import specifier for bundlers/runtimes support package.json exports is event-target-shim (again without /index. For bundlers/runtimes that don't, that doesn't change—it will just fall back to the file specified in main.

Under any circumstance that I can tell, @livekit/react-native-webrtc should NOT specify the /index subpath when importing event-target-shim.

helmturner avatar Jul 12 '25 03:07 helmturner

@helmturner so this issue has quite a bit of history behind it. Basically react-native-webrtc targets event-target-shim ^6.0.0, but react-native relies on ^5.0.0 and hasn't still upgraded it in quite a while.

https://github.com/react-native-webrtc/react-native-webrtc/issues/1503

The quirky workaround is what we've been relying on so that the different versions are resolved correctly. It isn't really feasible at the moment to downgrade event-target-shim back to ^5.0.0 to fix this.

If you've got a example repo available that repros the issue, that'd be very extremely helpful, as maybe we can figure out a different way to deal with this better.

davidliu avatar Jul 13 '25 07:07 davidliu