246 lines
10 KiB
JavaScript
246 lines
10 KiB
JavaScript
"use strict";
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.getConfiguration = getConfiguration;
|
|
exports.generatePackageListAsync = generatePackageListAsync;
|
|
exports.isAndroidProject = isAndroidProject;
|
|
exports.resolveModuleAsync = resolveModuleAsync;
|
|
exports.resolveExtraBuildDependenciesAsync = resolveExtraBuildDependenciesAsync;
|
|
exports.resolveGradlePropertyAsync = resolveGradlePropertyAsync;
|
|
exports.convertPackageToProjectName = convertPackageToProjectName;
|
|
exports.convertPackageWithGradleToProjectName = convertPackageWithGradleToProjectName;
|
|
exports.searchGradlePropertyFirst = searchGradlePropertyFirst;
|
|
const fs_1 = __importDefault(require("fs"));
|
|
const path_1 = __importDefault(require("path"));
|
|
const utils_1 = require("../../utils");
|
|
const ANDROID_PROPERTIES_FILE = 'gradle.properties';
|
|
const ANDROID_EXTRA_BUILD_DEPS_KEY = 'android.extraMavenRepos';
|
|
function getConfiguration(options) {
|
|
return options.buildFromSource ? { buildFromSource: options.buildFromSource } : undefined;
|
|
}
|
|
/**
|
|
* Generates Java file that contains all autolinked packages.
|
|
*/
|
|
async function generatePackageListAsync(modules, targetPath, namespace) {
|
|
const generatedFileContent = await generatePackageListFileContentAsync(modules, namespace);
|
|
const parentPath = path_1.default.dirname(targetPath);
|
|
if (!fs_1.default.existsSync(parentPath)) {
|
|
await fs_1.default.promises.mkdir(parentPath, { recursive: true });
|
|
}
|
|
await fs_1.default.promises.writeFile(targetPath, generatedFileContent, 'utf8');
|
|
}
|
|
function isAndroidProject(projectRoot) {
|
|
return (fs_1.default.existsSync(path_1.default.join(projectRoot, 'build.gradle')) ||
|
|
fs_1.default.existsSync(path_1.default.join(projectRoot, 'build.gradle.kts')));
|
|
}
|
|
async function resolveModuleAsync(packageName, revision) {
|
|
// TODO: Relative source dir should be configurable through the module config.
|
|
// Don't link itself... :D
|
|
if (packageName === '@unimodules/react-native-adapter') {
|
|
return null;
|
|
}
|
|
const plugins = (revision.config?.androidGradlePlugins() ?? []).map(({ id, group, sourceDir, applyToRootProject }) => ({
|
|
id,
|
|
group,
|
|
sourceDir: path_1.default.join(revision.path, sourceDir),
|
|
applyToRootProject: applyToRootProject ?? true,
|
|
}));
|
|
const defaultProjectName = convertPackageToProjectName(packageName);
|
|
const androidProjects = revision.config
|
|
?.androidProjects(defaultProjectName)
|
|
?.filter((project) => {
|
|
return !project.isDefault || isAndroidProject(path_1.default.join(revision.path, project.path));
|
|
});
|
|
// Just in case where the module doesn't have its own `build.gradle`/`settings.gradle`.
|
|
if (!androidProjects?.length) {
|
|
if (!plugins.length) {
|
|
return null;
|
|
}
|
|
return {
|
|
packageName,
|
|
plugins,
|
|
};
|
|
}
|
|
const projects = androidProjects.map((project) => {
|
|
const projectPath = path_1.default.join(revision.path, project.path);
|
|
const aarProjects = (project.gradleAarProjects ?? [])?.map((aarProject) => {
|
|
const projectName = `${defaultProjectName}$${aarProject.name}`;
|
|
const projectDir = path_1.default.join(projectPath, 'build', projectName);
|
|
return {
|
|
name: projectName,
|
|
aarFilePath: path_1.default.join(revision.path, aarProject.aarFilePath),
|
|
projectDir,
|
|
};
|
|
});
|
|
const { publication } = project;
|
|
const shouldUsePublicationScriptPath = project.shouldUsePublicationScriptPath
|
|
? path_1.default.join(revision.path, project.shouldUsePublicationScriptPath)
|
|
: undefined;
|
|
return {
|
|
name: project.name,
|
|
sourceDir: projectPath,
|
|
modules: project.modules ?? [],
|
|
...(shouldUsePublicationScriptPath ? { shouldUsePublicationScriptPath } : {}),
|
|
...(publication ? { publication } : {}),
|
|
...(aarProjects?.length > 0 ? { aarProjects } : {}),
|
|
};
|
|
});
|
|
const coreFeatures = revision.config?.coreFeatures() ?? [];
|
|
return {
|
|
packageName,
|
|
projects,
|
|
...(plugins?.length > 0 ? { plugins } : {}),
|
|
...(coreFeatures.length > 0 ? { coreFeatures } : {}),
|
|
};
|
|
}
|
|
async function resolveExtraBuildDependenciesAsync(projectNativeRoot) {
|
|
const extraMavenReposString = await resolveGradlePropertyAsync(projectNativeRoot, ANDROID_EXTRA_BUILD_DEPS_KEY);
|
|
if (extraMavenReposString) {
|
|
try {
|
|
return JSON.parse(extraMavenReposString);
|
|
}
|
|
catch { }
|
|
}
|
|
return null;
|
|
}
|
|
async function resolveGradlePropertyAsync(projectNativeRoot, propertyKey) {
|
|
const propsFile = path_1.default.join(projectNativeRoot, ANDROID_PROPERTIES_FILE);
|
|
try {
|
|
const contents = await fs_1.default.promises.readFile(propsFile, 'utf8');
|
|
const propertyValue = searchGradlePropertyFirst(contents, propertyKey);
|
|
if (propertyValue) {
|
|
return propertyValue;
|
|
}
|
|
}
|
|
catch { }
|
|
return null;
|
|
}
|
|
/**
|
|
* Generates the string to put into the generated package list.
|
|
*/
|
|
async function generatePackageListFileContentAsync(modules, namespace) {
|
|
// TODO: Instead of ignoring `expo` here, make the package class paths configurable from `expo-module.config.json`.
|
|
const packagesClasses = await findAndroidPackagesAsync(modules.filter((module) => module.packageName !== 'expo'));
|
|
const modulesClasses = await findAndroidModules(modules);
|
|
return `package ${namespace};
|
|
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import expo.modules.core.interfaces.Package;
|
|
import expo.modules.kotlin.modules.Module;
|
|
import expo.modules.kotlin.ModulesProvider;
|
|
|
|
public class ExpoModulesPackageList implements ModulesProvider {
|
|
private static class LazyHolder {
|
|
static final List<Package> packagesList = Arrays.<Package>asList(
|
|
${packagesClasses.map((packageClass) => ` new ${packageClass}()`).join(',\n')}
|
|
);
|
|
|
|
static final List<Class<? extends Module>> modulesList = Arrays.<Class<? extends Module>>asList(
|
|
${modulesClasses.map((moduleClass) => ` ${moduleClass}.class`).join(',\n')}
|
|
);
|
|
}
|
|
|
|
public static List<Package> getPackageList() {
|
|
return LazyHolder.packagesList;
|
|
}
|
|
|
|
@Override
|
|
public List<Class<? extends Module>> getModulesList() {
|
|
return LazyHolder.modulesList;
|
|
}
|
|
}
|
|
`;
|
|
}
|
|
function findAndroidModules(modules) {
|
|
const projects = modules.flatMap((module) => module.projects ?? []);
|
|
const modulesToProvide = projects.filter((project) => project.modules?.length > 0);
|
|
const classNames = [].concat(...modulesToProvide.map((module) => module.modules));
|
|
return classNames;
|
|
}
|
|
async function findAndroidPackagesAsync(modules) {
|
|
const classes = [];
|
|
const flattenedSourceDirList = [];
|
|
for (const module of modules) {
|
|
for (const project of module.projects ?? []) {
|
|
flattenedSourceDirList.push(project.sourceDir);
|
|
}
|
|
}
|
|
await Promise.all(flattenedSourceDirList.map(async (sourceDir) => {
|
|
for await (const file of (0, utils_1.scanFilesRecursively)(sourceDir)) {
|
|
if (!file.name.endsWith('Package.java') && !file.name.endsWith('Package.kt')) {
|
|
continue;
|
|
}
|
|
const fileContent = await fs_1.default.promises.readFile(file.path, 'utf8');
|
|
const packageRegex = (() => {
|
|
if (process.env.EXPO_SHOULD_USE_LEGACY_PACKAGE_INTERFACE) {
|
|
return /\bimport\s+org\.unimodules\.core\.(interfaces\.Package|BasePackage)\b/;
|
|
}
|
|
else {
|
|
return /\bimport\s+expo\.modules\.core\.(interfaces\.Package|BasePackage)\b/;
|
|
}
|
|
})();
|
|
// Very naive check to skip non-expo packages
|
|
if (!packageRegex.test(fileContent)) {
|
|
continue;
|
|
}
|
|
const classPathMatches = fileContent.match(/^package ([\w.]+)\b/m);
|
|
if (classPathMatches) {
|
|
const basename = path_1.default.basename(file.name, path_1.default.extname(file.name));
|
|
classes.push(`${classPathMatches[1]}.${basename}`);
|
|
}
|
|
}
|
|
}));
|
|
return classes.sort();
|
|
}
|
|
/**
|
|
* Converts the package name to Android's project name.
|
|
* `/` path will transform as `-`
|
|
*
|
|
* Example: `@expo/example` + `android/build.gradle` → `expo-example`
|
|
*/
|
|
function convertPackageToProjectName(packageName) {
|
|
return packageName.replace(/^@/g, '').replace(/\W+/g, '-');
|
|
}
|
|
/**
|
|
* Converts the package name and gradle file path to Android's project name.
|
|
* `$` to indicate subprojects
|
|
* `/` path will transform as `-`
|
|
*
|
|
* Example: `@expo/example` + `android/build.gradle` → `expo-example`
|
|
*
|
|
* Example: multiple projects
|
|
* - `expo-test` + `android/build.gradle` → `react-native-third-party`
|
|
* - `expo-test` + `subproject/build.gradle` → `react-native-third-party$subproject`
|
|
*/
|
|
function convertPackageWithGradleToProjectName(packageName, buildGradleFile) {
|
|
const name = convertPackageToProjectName(packageName);
|
|
const baseDir = path_1.default.dirname(buildGradleFile).replace(/\//g, '-');
|
|
return baseDir === 'android' ? name : `${name}$${baseDir}`;
|
|
}
|
|
/**
|
|
* Given the contents of a `gradle.properties` file,
|
|
* searches for a property with the given name.
|
|
*
|
|
* This function will return the first property found with the given name.
|
|
* The implementation follows config-plugins and
|
|
* tries to align the behavior with the `withGradleProperties` plugin.
|
|
*/
|
|
function searchGradlePropertyFirst(contents, propertyName) {
|
|
const lines = contents.split('\n');
|
|
for (let i = 0; i < lines.length; i++) {
|
|
const line = lines[i].trim();
|
|
if (line && !line.startsWith('#')) {
|
|
const eok = line.indexOf('=');
|
|
const key = line.slice(0, eok);
|
|
if (key === propertyName) {
|
|
const value = line.slice(eok + 1, line.length);
|
|
return value;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
//# sourceMappingURL=android.js.map
|