import javax.inject.Inject import org.gradle.api.DefaultTask import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.Internal import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction import org.gradle.process.ExecOperations import org.gradle.work.DisableCachingByDefault /** * Generates every configured RN plugin bundle before Android packages assets. * * Apply once from the host application module: * apply from: file("../../node_modules/@xuqm/rn-update/android/xuqm-bundles.gradle") */ @DisableCachingByDefault(because = "Metro maintains its own transform cache") abstract class GenerateXuqmBundlesTask extends DefaultTask { @Internal abstract DirectoryProperty getProjectRootDirectory() @InputFile @PathSensitive(PathSensitivity.RELATIVE) abstract RegularFileProperty getCliFile() @InputFile @PathSensitive(PathSensitivity.RELATIVE) abstract RegularFileProperty getConfigFile() @InputFile @PathSensitive(PathSensitivity.RELATIVE) abstract RegularFileProperty getPackageFile() @Input abstract Property getNodeExecutable() @Input abstract Property getAppVersion() @OutputDirectory abstract DirectoryProperty getOutputDirectory() @Inject abstract ExecOperations getExecOperations() @TaskAction void generate() { def embeddedBundles = outputDirectory.get().dir("rn-bundles").asFile execOperations.exec { workingDir(projectRootDirectory.get().asFile) commandLine( nodeExecutable.get(), cliFile.get().asFile.absolutePath, "embed", "android", "--output", embeddedBundles.absolutePath, "--app-version", appVersion.get(), ) }.assertNormalExitValue() } } if (!plugins.hasPlugin("com.android.application")) { throw new GradleException("xuqm-bundles.gradle must be applied to an Android application module") } def xuqmProjectRoot = rootProject.projectDir.parentFile def xuqmUseMetro = providers.gradleProperty("USE_METRO") .orElse(providers.environmentVariable("USE_METRO")) .map { it.toBoolean() } .orElse(false) def prepareXuqmBundles = tasks.register( "prepareXuqmEmbeddedBundles", GenerateXuqmBundlesTask, ) { group = "xuqm" description = "Builds and embeds all configured Xuqm RN plugin bundles." projectRootDirectory.set(xuqmProjectRoot) cliFile.set(new File(xuqmProjectRoot, "node_modules/@xuqm/rn-update/scripts/xuqm-rn.mjs")) configFile.set(new File(xuqmProjectRoot, "xuqm.config.json")) packageFile.set(new File(xuqmProjectRoot, "package.json")) nodeExecutable.set(System.getenv("NODE_BINARY") ?: "node") appVersion.set(providers.provider { def version = android.defaultConfig.versionName if (version == null || version.toString().trim().isEmpty()) { throw new GradleException("Android defaultConfig.versionName is required") } version.toString() }) outputDirectory.set(layout.buildDirectory.dir("generated/xuqm/assets")) outputs.upToDateWhen { false } } androidComponents.onVariants(androidComponents.selector().all()) { variant -> if (variant.buildType == "release" || !xuqmUseMetro.get()) { variant.sources.assets?.addGeneratedSourceDirectory( prepareXuqmBundles, { task -> task.outputDirectory }, ) } } tasks.configureEach { task -> if (task.name ==~ /^createBundle.*ReleaseJsAndAssets$/) { // Xuqm hosts load startup/common/app/plugin bundles explicitly. A second // monolithic index.android.bundle is both redundant and unsafe. task.enabled = false } }