Could not find declaration file for module 'X' Error

I have error in importing @wowmaking/react-native-iron-source. It seems like this module might not provide typings, which means that you need to declare the module in a file with a .d.ts extension. I am supposed to provide types in my tsconfig.json file according to this blogpost. But, cannot find tsconofig.json file in expo. Should I create it myself? Where should I put this file? Sorry that this maybe a very dump question. I don’t know how to use TS…

Hi @rachellauyui

Normally if you start with a JavaScript Expo project Expo can generate the tsconfig.json and add the necessary devDependencies. You just need to e.g. rename App.js to App.ts (or tsx) and then run expo start:

/tmp/sdk-45$ git mv App.js App.ts
/tmp/sdk-45$ expo start
Starting project at /tmp/sdk-45
Developer tools running on http://localhost:19002
✔ It looks like you're trying to use TypeScript but don't have the required
dependencies installed. Would you like to install typescript, @types/react,
@types/react-native? … yes

✔ Installed typescript@~4.3.5, @types/react@~17.0.21, @types/react-native@~0.67.6

TypeScript: A tsconfig.json has been auto-generated

Starting Metro Bundler
[...]
diff --git package.json package.json
index 958856d..ce6d78a 100644
--- package.json
+++ package.json
@@ -18,7 +18,10 @@
     "react-native-web": "0.17.7"
   },
   "devDependencies": {
-    "@babel/core": "^7.12.9"
+    "@babel/core": "^7.12.9",
+    "@types/react": "~17.0.21",
+    "@types/react-native": "~0.67.6",
+    "typescript": "~4.3.5"
   },
   "private": true
 }

The default tsconfig.json created by Expo looks like this:

{
  "compilerOptions": {},
  "extends": "expo/tsconfig.base"
}

You could also do the above manually.

@wodin Hi Michael! Thank you so much for the quick reply! Before I even try your steps, suddenly the Iron Source works and a banner pops up in Android. However, still no banner in iOS with an error of “unhandled promise”. But since the initiation of Iron Source works in both Android and iOS, it seems like though the warning, the App is able to retrieve from this module alright.

Since I need to write plugins in order to use this module, I wonder if I’ve made some mistakes in the iOS plugins?

Android plugin:

const { withAppBuildGradle } = require("@expo/config-plugins");

module.exports = function dependenciesPlugin(expoConfig) {
  return withAppBuildGradle(expoConfig, async (config) => {
    config.modResults.contents = applyImplementation(
      config.modResults.contents
    );
    return config;
  });
};

function applyImplementation(appBuildGradle) {
  const dependenciesImplementation = `implementation 'com.ironsource.sdk:mediationsdk:7.2.2.1'
  implementation 'com.ironsource.adapters:applovinadapter:4.3.32'
  implementation 'com.applovin:applovin-sdk:11.4.2'
  implementation 'com.ironsource.adapters:facebookadapter:4.3.36'
  implementation 'com.facebook.android:audience-network-sdk:6.11.0'`;

  // Make sure the project does not have the dependency already
  if (!appBuildGradle.includes(dependenciesImplementation)) {
    return appBuildGradle.replace(
      /dependencies\s?{/,
      `
      repositories {
        mavenCentral()
        maven {url 'https://android-sdk.is.com/'} 
     
     }
     
      dependencies {
    ${dependenciesImplementation}`
    );
  }

  return appBuildGradle;
}

The iOS plugin:

const {
  withDangerousMod,
  withPlugins,
  withInfoPlist,
} = require("@expo/config-plugins");
const { resolve } = require("path");
const { readFileSync, writeFileSync } = require("fs");

// Add podfiles
function withIronSourcePod(config) {
  return withDangerousMod(config, [
    "ios",
    (cfg) => {
      const { platformProjectRoot } = cfg.modRequest;
      const podfile = resolve(platformProjectRoot, "Podfile");
      const contents = readFileSync(podfile, "utf-8");
      const lines = contents.split("\n");
      const index = lines.findIndex((line) =>
        /\s+use_expo_modules!/.test(line)
      );
      console.log({ index });

      writeFileSync(
        podfile,
        [
          ...lines.slice(0, 20),
          `   pod 'IronSourceSDK','7.2.2.1'
              pod 'IronSourceAppLovinAdapter','4.3.32.0'
              pod 'IronSourceFacebookAdapter','4.3.36.0'
              `,
          ...lines.slice(20),
        ].join("\n")
      );

      return cfg;
    },
  ]);
}

function withIronSourcePlist(config) {
  return withInfoPlist(config, (config) => {
    config.modResults.contents = config.modResults.contents.replace(
      "<dict>",
      `<dict>

      <key>SKAdNetworkItems</key>
<array>
   <dict>
      <key>SKAdNetworkIdentifier</key>
      <string>su67r6k2v3.skadnetwork</string>
   </dict>
</array>`
    );
  });
}

function withIronSource(config) {
  return withPlugins(config, [withIronSourcePod]);
}

module.exports = withIronSource;

Something else may or may not be related: once I insert these plugins, my App icon and Splash Screen disappeared after EAS Build, in both Android and iOS. I read your replies in another post, and I did clean up all android / ios files and revert back gitignore. I am pretty sure I am still in “Managed” mode. And since App icon and Splash Screen are stored in App.json, my plugins which amend app/build.gradle, pod and InfoPlist should not interfere with them… right?

Super weird day!

I have not had a close look at your config plugins (and I wouldn’t call myself an expert anyway) but the iOS one looks like it could be a bit brittle. i.e. it might break if you upgrade to a later Expo SDK or if something else makes changes to the Podfile.

By the way, you don’t need a config plugin for adding stuff to Info.plist. You can do that in app.json. If you do want to do it in a config plugin you should not make changes to the raw text, but rather do it like the example here:

That’s unexpected. If you do not have android and ios directories then that is the managed workflow.

Maybe try running expo prebuild without your conflg plugins and again with them and compare the results to see that they are as you expect them to be. Off the top of my head I don’t have a better suggestion :confused:

@wodin Hi Michael! To update, thank you very much for your advice to build without plugins. It turns out Iron Source will initiate no matter what, and I found that it is because I created app.config.json that make the icon and splash screen disappeared!! Expo inisist to put name, bundle id etc in app.config.json. I wonder if I should put these information in app.config.json only but not in app.json? Any way, I cancelled app.config.json and put the plugin in app.json, and then App icon and splash worked fine!

But no luck with iOS though. I have amended my plugin on info.plist as suggested:

function withIronSourcePlist(config) {
  return withInfoPlist(config, (config) => {
    config.modResults.SKAdNetworkItems = [];

    config.modResults.contents = config.modResults.SKAdNetworkItems.push({
      SKAdNetworkIdentifier: "su67r6k2v3.skadnetwork",
    });
    return config;
  });
}

But still, iOS failed to load banner with “unhandled promise” error… Really have no ideas what is wrong!

Hi @rachellauyui

app.config.json and app.json are basically the same thing. I don’t know off hand what happens if you have both of those. I suspect Expo will use app.config.json and ignore app.json in that case, which given what you say above would explain why your icon and splash screen disappeared.

:+1:

I generally stick with app.json myself and have never had any problems with using that instead of app.config.json. I have on occasion used app.config.js if e.g. I wanted different a package/bundleID for dev vs. preview vs. prod, but when I do that I still keep most stuff in app.json and do the bare minimum in app.config.js like this:

(This one was an experiment before BuildProperties was announced):

// app.config.js
import { AndroidConfig } from "@expo/config-plugins";

export default function ({ config }) {
  return {
    ...config,
    plugins: [
      [
        AndroidConfig.Version.withBuildScriptExtMinimumVersion,
        {
          name: "compileSdkVersion",
          minVersion: 31,
        },
      ],
      [
        AndroidConfig.Version.withBuildScriptExtMinimumVersion,
        {
          name: "targetSdkVersion",
          minVersion: 31,
        },
      ],
    ],
  };
}

I’ll have a closer look at your config plugin and see if I can figure out what’s wrong.

Someone pointed me at the following example of adding pods to a Podfile:

I haven’t looked at it closely enough to see how to adapt it to your case, but maybe you can figure it out before I do :slight_smile:

@wodin Hi Michael! The craziest thing happened this morning! Suddenly everything works, banner and interstitial work both in Android and iOS. I have no ideas why suddently it is all ok. Maybe IronSource needs some more time to update their Ads?

And thank you very much for the example. I will definitely study it. This definitely look much more robust than mine! Having config plugin feels like super power, isn’t it? Expo will no longer confined to “managed” workflow and can use modules which modify native code!!

1 Like