react-native-acoustic-connect-beta
Version:
BETA: React native plugin for Acoustic Connect
160 lines • 8.56 kB
JavaScript
;
// 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.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.withConnectAndroidGoogleServicesMatch = exports.withConnectAndroidConfig = exports.appendConfigGradleApply = exports.CONFIG_GRADLE_APPLY = void 0;
const config_plugins_1 = require("@expo/config-plugins");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
// 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.
exports.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.
*/
function appendConfigGradleApply(contents) {
if (contents.includes('/config.gradle')) {
return contents;
}
return `${contents}\n\n${exports.CONFIG_GRADLE_APPLY}\n`;
}
exports.appendConfigGradleApply = appendConfigGradleApply;
/**
* 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).
*/
const withConnectAndroidConfig = (config) => (0, config_plugins_1.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` +
` ${exports.CONFIG_GRADLE_APPLY}`);
return cfg;
}
cfg.modResults.contents = appendConfigGradleApply(cfg.modResults.contents);
return cfg;
});
exports.withConnectAndroidConfig = withConnectAndroidConfig;
/** True when ConnectConfig.json at `projectRoot` has Connect.PushEnabled === true. */
function isPushEnabled(projectRoot) {
var _a;
const configPath = path.join(projectRoot, 'ConnectConfig.json');
if (!fs.existsSync(configPath))
return false;
try {
const parsed = JSON.parse(fs.readFileSync(configPath, 'utf8'));
return ((_a = parsed.Connect) === null || _a === void 0 ? void 0 : _a.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).
*/
const withConnectAndroidGoogleServicesMatch = (config) => (0, config_plugins_1.withDangerousMod)(config, [
'android',
async (cfg) => {
var _a, _b, _c, _d, _e;
const projectRoot = (_a = cfg._internal) === null || _a === void 0 ? void 0 : _a.projectRoot;
if (!projectRoot || !isPushEnabled(projectRoot))
return cfg;
const androidPackage = (_b = cfg.android) === null || _b === void 0 ? void 0 : _b.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 = (_d = (_c = cfg.android) === null || _c === void 0 ? void 0 : _c.googleServicesFile) !== null && _d !== void 0 ? _d : '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;
try {
parsed = JSON.parse(fs.readFileSync(gsPath, 'utf8'));
}
catch {
return cfg; // malformed google-services.json — doctor/build will report it
}
const packages = ((_e = parsed.client) !== null && _e !== void 0 ? _e : [])
.map((c) => { var _a, _b; return (_b = (_a = c.client_info) === null || _a === void 0 ? void 0 : _a.android_client_info) === null || _b === void 0 ? void 0 : _b.package_name; })
.filter((p) => 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;
},
]);
exports.withConnectAndroidGoogleServicesMatch = withConnectAndroidGoogleServicesMatch;
//# sourceMappingURL=withConnectAndroidConfig.js.map