diff --git a/packages/cli-platform-android/src/config/__tests__/getAndroidProject.test.ts b/packages/cli-platform-android/src/config/__tests__/getAndroidProject.test.ts
index 25fed58ba..ed208f185 100644
--- a/packages/cli-platform-android/src/config/__tests__/getAndroidProject.test.ts
+++ b/packages/cli-platform-android/src/config/__tests__/getAndroidProject.test.ts
@@ -6,7 +6,11 @@
*
*/
-import {validatePackageName} from '../getAndroidProject';
+import {
+ validatePackageName,
+ parsePackageNameFromAndroidManifestFile,
+ parseNamespaceFromBuildGradleFile,
+} from '../getAndroidProject';
describe('android::getAndroidProject', () => {
const expectedResults = {
@@ -34,3 +38,70 @@ describe('android::getAndroidProject', () => {
});
});
});
+
+describe('parsePackageNameFromAndroidManifestFile', () => {
+ it('should parse package name from AndroidManifest', () => {
+ const androidManifest = `
+
+`;
+
+ expect(parsePackageNameFromAndroidManifestFile(androidManifest)).toBe(
+ 'com.example.app',
+ );
+ });
+
+ it('should return null if package name is missing from AndroidManifest', () => {
+ const androidManifest = `
+
+`;
+
+ expect(parsePackageNameFromAndroidManifestFile(androidManifest)).toBeNull();
+ });
+});
+
+describe('parseNamespaceFromBuildGradleFile', () => {
+ // Test that it can parse a namespace from a build.gradle file
+ it('should parse namespace from build.gradle', () => {
+ const buildGradle = `apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 31
+ namespace "com.example.app"
+}`;
+
+ expect(parseNamespaceFromBuildGradleFile(buildGradle)).toBe(
+ 'com.example.app',
+ );
+ });
+
+ // Test that it can parse a namespace from a build.gradle.kts file
+ it('should parse namespace from build.gradle.kts', () => {
+ const buildGradle = `plugins {
+ id 'com.android.application'
+}
+
+android {
+ compileSdk = 31
+ namespace = "com.example.app"
+}`;
+
+ expect(parseNamespaceFromBuildGradleFile(buildGradle)).toBe(
+ 'com.example.app',
+ );
+ });
+
+ // Test that it returns null if namespace is missing from build.gradle
+ it('should return null if namespace is missing from build.gradle', () => {
+ const buildGradle = `apply plugin: 'com.android.application'
+android {
+ compileSdkVersion 31
+}`;
+
+ expect(parseNamespaceFromBuildGradleFile(buildGradle)).toBeNull();
+ });
+});
diff --git a/packages/cli-platform-android/src/config/findBuildGradle.ts b/packages/cli-platform-android/src/config/findBuildGradle.ts
new file mode 100644
index 000000000..1372c4118
--- /dev/null
+++ b/packages/cli-platform-android/src/config/findBuildGradle.ts
@@ -0,0 +1,15 @@
+import fs from 'fs';
+import path from 'path';
+
+export function findBuildGradle(sourceDir: string) {
+ const buildGradlePath = path.join(sourceDir, 'build.gradle');
+ const buildGradleKtsPath = path.join(sourceDir, 'build.gradle.kts');
+
+ if (fs.existsSync(buildGradlePath)) {
+ return buildGradlePath;
+ } else if (fs.existsSync(buildGradleKtsPath)) {
+ return buildGradleKtsPath;
+ } else {
+ return null;
+ }
+}
diff --git a/packages/cli-platform-android/src/config/getAndroidProject.ts b/packages/cli-platform-android/src/config/getAndroidProject.ts
index 15e023cad..f30596c1b 100644
--- a/packages/cli-platform-android/src/config/getAndroidProject.ts
+++ b/packages/cli-platform-android/src/config/getAndroidProject.ts
@@ -17,23 +17,54 @@ export function getAndroidProject(config: Config) {
}
/**
- * Get the package name of the running React Native app
- * @param config
+ * Get the package name/namespace of the running React Native app
+ * @param manifestPath The path to the AndroidManifest.xml
+ * @param buildGradlePath The path to the build.gradle[.kts] file.
*/
-export function getPackageName(manifestPath: string) {
+export function getPackageName(
+ manifestPath: string,
+ buildGradlePath: string | null,
+) {
const androidManifest = fs.readFileSync(manifestPath, 'utf8');
- let packageNameMatchArray = androidManifest.match(/package="(.+?)"/);
- if (!packageNameMatchArray || packageNameMatchArray.length === 0) {
+ const packageNameFromManifest = parsePackageNameFromAndroidManifestFile(
+ androidManifest,
+ );
+ let packageName;
+ if (packageNameFromManifest) {
+ // We got the package from the AndroidManifest.xml
+ packageName = packageNameFromManifest;
+ } else if (buildGradlePath) {
+ // We didn't get the package from the AndroidManifest.xml,
+ // so we'll try to get it from the build.gradle[.kts] file
+ // via the namespace field.
+ const buildGradle = fs.readFileSync(buildGradlePath, 'utf8');
+ const namespace = parseNamespaceFromBuildGradleFile(buildGradle);
+ if (namespace) {
+ packageName = namespace;
+ } else {
+ throw new CLIError(
+ `Failed to build the app: No package name found.
+ We couldn't parse the namespace from your build.gradle[.kts] file at ${chalk.underline.dim(
+ `${buildGradlePath}`,
+ )}
+ and nor your package in the AndroidManifest at ${chalk.underline.dim(
+ `${manifestPath}`,
+ )}
+ `,
+ );
+ }
+ } else {
throw new CLIError(
- `Failed to build the app: No package name found. Found errors in ${chalk.underline.dim(
+ `Failed to build the app: No package name found.
+ We failed to parse your AndroidManifest at ${chalk.underline.dim(
`${manifestPath}`,
- )}`,
+ )}
+ and we couldn't find your build.gradle[.kts] file.
+ `,
);
}
- let packageName = packageNameMatchArray[1];
-
if (!validatePackageName(packageName)) {
logger.warn(
`Invalid application's package name "${chalk.bgRed(
@@ -46,6 +77,27 @@ export function getPackageName(manifestPath: string) {
return packageName;
}
+export function parsePackageNameFromAndroidManifestFile(
+ androidManifest: string,
+) {
+ const matchArray = androidManifest.match(/package="(.+?)"/);
+ if (matchArray && matchArray.length > 0) {
+ return matchArray[1];
+ } else {
+ return null;
+ }
+}
+
+export function parseNamespaceFromBuildGradleFile(buildGradle: string) {
+ // search for namespace = inside the build.gradle file via regex
+ const matchArray = buildGradle.match(/namespace\s*[=]*\s*"(.+?)"/);
+ if (matchArray && matchArray.length > 0) {
+ return matchArray[1];
+ } else {
+ return null;
+ }
+}
+
// Validates that the package name is correct
export function validatePackageName(packageName: string) {
return /^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/i.test(packageName);
diff --git a/packages/cli-platform-android/src/config/index.ts b/packages/cli-platform-android/src/config/index.ts
index be515f4c8..7dcc9a00d 100644
--- a/packages/cli-platform-android/src/config/index.ts
+++ b/packages/cli-platform-android/src/config/index.ts
@@ -20,6 +20,7 @@ import {
import {getPackageName} from './getAndroidProject';
import {findLibraryName} from './findLibraryName';
import {findComponentDescriptors} from './findComponentDescriptors';
+import {findBuildGradle} from './findBuildGradle';
/**
* Gets android project config by analyzing given folder and taking some
@@ -42,12 +43,14 @@ export function projectConfig(
const manifestPath = userConfig.manifestPath
? path.join(sourceDir, userConfig.manifestPath)
: findManifest(path.join(sourceDir, appName));
+ const buildGradlePath = findBuildGradle(sourceDir);
if (!manifestPath) {
return null;
}
- const packageName = userConfig.packageName || getPackageName(manifestPath);
+ const packageName =
+ userConfig.packageName || getPackageName(manifestPath, buildGradlePath);
if (!packageName) {
throw new Error(`Package name not found in ${manifestPath}`);
@@ -96,12 +99,14 @@ export function dependencyConfig(
const manifestPath = userConfig.manifestPath
? path.join(sourceDir, userConfig.manifestPath)
: findManifest(sourceDir);
+ const buildGradlePath = path.join(sourceDir, 'build.gradle');
if (!manifestPath) {
return null;
}
- const packageName = userConfig.packageName || getPackageName(manifestPath);
+ const packageName =
+ userConfig.packageName || getPackageName(manifestPath, buildGradlePath);
const packageClassName = findPackageClassName(sourceDir);
/**