UNPKG

react-native-acoustic-connect-beta

Version:

BETA: React native plugin for Acoustic Connect

157 lines (144 loc) 6.8 kB
// Copyright (C) 2026 Acoustic, L.P. All rights reserved. // // NOTICE: This file contains material that is confidential and proprietary to // Acoustic, L.P. and/or other developers. No license is granted under any // intellectual or industrial property rights of Acoustic, L.P. except as may // be provided in an agreement with Acoustic, L.P. Any unauthorized copying or // distribution of content from this file is prohibited. import { withAppBuildGradle, withDangerousMod, type ConfigPlugin, } from '@expo/config-plugins' import * as fs from 'fs' import * as path from 'path' import type { ConnectPluginProps } from './withConnectNSE' // The Gradle snippet that runs the SDK's config.gradle. config.gradle reads // the consumer app's ConnectConfig.json (`$project.rootDir/../ConnectConfig.json`) // and writes AppKey / PostMessageUrl / KillSwitchUrl (and the Tealeaf + EOCore // configs) into the SDK module's `src/main/assets/*` at Gradle configuration // time — so the collector the app reports to is driven by ConnectConfig.json. // // This mirrors the line `scripts/gradleParser.js` appends for the bare-workflow // sample (Examples/bare-workflow/android/app/build.gradle). The Expo Android // project is gitignored and regenerated by `expo prebuild`, so the line cannot // be committed there — this mod re-injects it on every prebuild instead. // // The ':react-native-acoustic-connect-beta' Gradle project is provided by // React Native autolinking and resolves in the Expo app the same way it does // in bare-workflow. export const CONFIG_GRADLE_APPLY = `apply from: project(':react-native-acoustic-connect-beta')` + `.projectDir.getPath() + "/config.gradle"` /** * Append the config.gradle `apply from:` line to an app `build.gradle` body. * * Idempotent: a no-op when the line is already present, so repeated prebuilds * (and a prebuild over an already-patched, non-`--clean` project) don't stack * duplicate applies. */ export function appendConfigGradleApply(contents: string): string { if (contents.includes('/config.gradle')) { return contents } return `${contents}\n\n${CONFIG_GRADLE_APPLY}\n` } /** * Expo config mod: wire the SDK's config.gradle into the generated Android * app build so ConnectConfig.json values reach the native assets at build * time. Without it, the Android SDK ships its committed default collector * config and the app reports to the wrong endpoint (iOS is unaffected — it * uses AcousticConnectRNConfig.json via a separate flow). */ export const withConnectAndroidConfig: ConfigPlugin<ConnectPluginProps> = ( config ) => withAppBuildGradle(config, (cfg) => { // The RN/Expo template app build.gradle is Groovy. If a future template // switches to the Kotlin DSL the append heuristic no longer applies — // warn and skip rather than corrupt the file. if (cfg.modResults.language !== 'groovy') { console.warn( `[react-native-acoustic-connect-beta] Skipping Android config ` + `propagation: app/build.gradle is '${cfg.modResults.language}', ` + `expected 'groovy'. Add the following line manually:\n` + ` ${CONFIG_GRADLE_APPLY}` ) return cfg } cfg.modResults.contents = appendConfigGradleApply(cfg.modResults.contents) return cfg }) /** True when ConnectConfig.json at `projectRoot` has Connect.PushEnabled === true. */ function isPushEnabled(projectRoot: string): boolean { const configPath = path.join(projectRoot, 'ConnectConfig.json') if (!fs.existsSync(configPath)) return false try { const parsed = JSON.parse(fs.readFileSync(configPath, 'utf8')) as { Connect?: { PushEnabled?: unknown } } return parsed.Connect?.PushEnabled === true } catch { return false } } /** * Fail fast at prebuild when push is enabled but `android.package` is absent * from the configured `google-services.json`. FCM matches its client by package * name, so a mismatch makes Gradle fail much later at * `:app:processDebugGoogleServices` with the opaque "No matching client found * for package name …" — and only after a full prebuild + Gradle config. Surface * it here, at the moment the native project is (re)generated, with the exact fix * (CA-144135 §10b). `acoustic-connect doctor` performs the same check up front; * this is the guard for a developer who runs `expo run:android` directly. * * Only the genuine MISMATCH throws. A missing package / googleServicesFile is * left to `doctor` and the build itself (this mod doesn't duplicate those). */ export const withConnectAndroidGoogleServicesMatch: ConfigPlugin< ConnectPluginProps > = (config) => withDangerousMod(config, [ 'android', async (cfg) => { const projectRoot = cfg._internal?.projectRoot if (!projectRoot || !isPushEnabled(projectRoot)) return cfg const androidPackage = cfg.android?.package if (!androidPackage) return cfg // Most Expo + FCM consumers omit android.googleServicesFile and rely on // the default ./google-services.json at the project root. Mirror that // default (and `acoustic-connect doctor`'s) so the match check isn't // silently skipped for the common case. const gsFile = cfg.android?.googleServicesFile ?? 'google-services.json' const gsPath = path.isAbsolute(gsFile) ? gsFile : path.join(projectRoot, gsFile) if (!fs.existsSync(gsPath)) return cfg // missing file is doctor's / the build's concern let parsed: { client?: Array<{ client_info?: { android_client_info?: { package_name?: string } } }> } try { parsed = JSON.parse(fs.readFileSync(gsPath, 'utf8')) } catch { return cfg // malformed google-services.json — doctor/build will report it } const packages = (parsed.client ?? []) .map((c) => c.client_info?.android_client_info?.package_name) .filter((p): p is string => typeof p === 'string') if (packages.length > 0 && !packages.includes(androidPackage)) { throw new Error( `[react-native-acoustic-connect-beta] android.package "${androidPackage}" ` + `has no matching client in ${gsFile} (it has: ${packages.join(', ')}).\n\n` + `FCM matches its client by package name, so the Android build would ` + `fail at :app:processDebugGoogleServices. Register "${androidPackage}" in ` + `the same Firebase project and re-download google-services.json, or set ` + `app.json android.package to one of the registered packages.\n` + `Note: changing android.package requires a clean prebuild — ` + `\`npx expo prebuild --platform android --clean\`.` ) } return cfg }, ])