Skip to content

Instantly share code, notes, and snippets.

@ridvanaltun
Last active October 26, 2024 21:11
Show Gist options
  • Save ridvanaltun/5bf4e072e8db7a4ecd93770c542b1387 to your computer and use it in GitHub Desktop.
Save ridvanaltun/5bf4e072e8db7a4ecd93770c542b1387 to your computer and use it in GitHub Desktop.
Auto-linking configuration for React-Native 0.75.4 Android Instant App

I'm following this instruction for reduce Instant App bundle size: https://github.com/codibly/app-clip-instant-app-react-native/blob/b33afbf0fb559bcbfe464be31c6297c61ce117ac/Handling-Size-React-Native-InstantApp.md

The author suggesting disable autolinking for Instant Apps to reduce app size. Unfortunately the document outdated and it's hard to apply for newer React-Native versions. I tried to apply same thing and failed.

My approach is keeping the autolinking mechanism and use react-native.config.js file to select which libraries will be linked or not.

Following changes are necessary:

  1. Mark don't needed libraries with instantApp key in react-native.config.js file:
module.exports = {
  dependencies: {
    ['react-native-camera']: {
      instantApp: false,
    },
  },
};
  1. Update instantApp/build.gradle file:
react {
  // ...
  
  instantApp = true
  
  /* Autolinking */
  autolinkLibrariesWithApp(true)
}
  1. Update app/build.gradle file:
react {
  // ...
    
  /* Autolinking */
  autolinkLibrariesWithApp(false)
}
  1. Update @react-native-community/cli-config:

node_modules/@react-native-community/cli-config/build/schema.js

Go to file: https://gist.github.com/ridvanaltun/5bf4e072e8db7a4ecd93770c542b1387#file-schema-diff

  1. Update ReactExtension.kt in @react-native/gradle-plugin:

node_modules/@react-native/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactExtension.kt

Go to file: https://gist.github.com/ridvanaltun/5bf4e072e8db7a4ecd93770c542b1387#file-reactextension-diff

  1. Update ReactPlugin.kt in @react-native/gradle-plugin:

node_modules/@react-native/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt

Go to file: https://gist.github.com/ridvanaltun/5bf4e072e8db7a4ecd93770c542b1387#file-reactplugin-diff

  1. Update GeneratePackageListTask.kt in @react-native/gradle-plugin:

node_modules/@react-native/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GeneratePackageListTask.kt

Go to file: https://gist.github.com/ridvanaltun/5bf4e072e8db7a4ecd93770c542b1387#file-generatepackagelisttask-diff

  1. Update ModelAutolinkingDependenciesJson.kt in @react-native/gradle-plugin:

node_modules/@react-native/gradle-plugin/shared/src/main/kotlin/com/facebook/react/model/ModelAutolinkingDependenciesJson.kt

Go to file: https://gist.github.com/ridvanaltun/5bf4e072e8db7a4ecd93770c542b1387#file-modelautolinkingdependenciesjson-diff

  1. After all this changes we need patch all of them
npx patch-package @react-native-community/cli-config
npx patch-package @react-native/gradle-plugin

I think thats all.

/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.tasks
import com.facebook.react.model.ModelAutolinkingConfigJson
import com.facebook.react.model.ModelAutolinkingDependenciesPlatformAndroidJson
import com.facebook.react.utils.JsonUtils
import java.io.File
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
abstract class GeneratePackageListTask : DefaultTask() {
init {
group = "react"
}
@get:InputFile abstract val autolinkInputFile: RegularFileProperty
+ @Input
+ var instantApp: Boolean = false
@get:OutputDirectory abstract val generatedOutputDirectory: DirectoryProperty
@TaskAction
fun taskAction() {
val model =
JsonUtils.fromAutolinkingConfigJson(autolinkInputFile.get().asFile)
?: error(
"""
RNGP - Autolinking: Could not parse autolinking config file:
${autolinkInputFile.get().asFile.absolutePath}
The file is either missing or not containing valid JSON so the build won't succeed.
"""
.trimIndent())
val packageName =
model.project?.android?.packageName
?: error(
"RNGP - Autolinking: Could not find project.android.packageName in react-native config output! Could not autolink packages without this field.")
val androidPackages = filterAndroidPackages(model)
val packageImports = composePackageImports(packageName, androidPackages)
val packageClassInstance = composePackageInstance(packageName, androidPackages)
val generatedFileContents = composeFileContent(packageImports, packageClassInstance)
val outputDir = generatedOutputDirectory.get().asFile
outputDir.mkdirs()
File(outputDir, GENERATED_FILENAME).apply {
parentFile.mkdirs()
writeText(generatedFileContents)
}
}
internal fun composePackageImports(
packageName: String,
packages: Map<String, ModelAutolinkingDependenciesPlatformAndroidJson>
) =
packages.entries.joinToString("\n") { (name, dep) ->
val packageImportPath =
requireNotNull(dep.packageImportPath) {
"RNGP - Autolinking: Missing `packageImportPath` in `config` for dependency $name. This is required to generate the autolinking package list."
}
"// $name\n${interpolateDynamicValues(packageImportPath, packageName)}"
}
internal fun composePackageInstance(
packageName: String,
packages: Map<String, ModelAutolinkingDependenciesPlatformAndroidJson>
) =
if (packages.isEmpty()) {
""
} else {
",\n " +
packages.entries.joinToString(",\n ") { (name, dep) ->
val packageInstance =
requireNotNull(dep.packageInstance) {
"RNGP - Autolinking: Missing `packageInstance` in `config` for dependency $name. This is required to generate the autolinking package list."
}
interpolateDynamicValues(packageInstance, packageName)
}
}
internal fun filterAndroidPackages(
model: ModelAutolinkingConfigJson?
): Map<String, ModelAutolinkingDependenciesPlatformAndroidJson> {
val packages = model?.dependencies?.values ?: emptyList()
return packages
.filter { it.platforms?.android != null }
+ .filter { if (instantApp) it?.instantApp !== false else true }
// The pure C++ dependencies won't have a .java/.kt file to import
.filterNot { it.platforms?.android?.isPureCxxDependency == true }
.associate { it.name to checkNotNull(it.platforms?.android) }
}
internal fun composeFileContent(packageImports: String, packageClassInstance: String): String =
generatedFileContentsTemplate
.replace("{{ packageImports }}", packageImports)
.replace("{{ packageClassInstances }}", packageClassInstance)
companion object {
const val GENERATED_FILENAME = "com/facebook/react/PackageList.java"
/**
* Before adding the package replacement mechanism, BuildConfig and R classes were imported
* automatically into the scope of the file. We want to replace all non-FQDN references to those
* classes with the package name of the MainApplication.
*
* We want to match "R" or "BuildConfig":
* - new Package(R.string…),
* - Module.configure(BuildConfig);
* ^ hence including (BuildConfig|R)
* but we don't want to match "R":
* - new Package(getResources…),
* - new PackageR…,
* - new Royal…,
* ^ hence excluding \w before and after matches
* and "BuildConfig" that has FQDN reference:
* - Module.configure(com.acme.BuildConfig);
* ^ hence excluding . before the match.
*/
internal fun interpolateDynamicValues(input: String, packageName: String): String =
input.replace(Regex("([^.\\w])(BuildConfig|R)(\\W)")) { match ->
val (prefix, className, suffix) = match.destructured
"${prefix}${packageName}.${className}${suffix}"
}
// language=java
val generatedFileContentsTemplate =
"""
package com.facebook.react;
import android.app.Application;
import android.content.Context;
import android.content.res.Resources;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainPackageConfig;
import com.facebook.react.shell.MainReactPackage;
import java.util.Arrays;
import java.util.ArrayList;
{{ packageImports }}
public class PackageList {
private Application application;
private ReactNativeHost reactNativeHost;
private MainPackageConfig mConfig;
public PackageList(ReactNativeHost reactNativeHost) {
this(reactNativeHost, null);
}
public PackageList(Application application) {
this(application, null);
}
public PackageList(ReactNativeHost reactNativeHost, MainPackageConfig config) {
this.reactNativeHost = reactNativeHost;
mConfig = config;
}
public PackageList(Application application, MainPackageConfig config) {
this.reactNativeHost = null;
this.application = application;
mConfig = config;
}
private ReactNativeHost getReactNativeHost() {
return this.reactNativeHost;
}
private Resources getResources() {
return this.getApplication().getResources();
}
private Application getApplication() {
if (this.reactNativeHost == null) return this.application;
return this.reactNativeHost.getApplication();
}
private Context getApplicationContext() {
return this.getApplication().getApplicationContext();
}
public ArrayList<ReactPackage> getPackages() {
return new ArrayList<>(Arrays.<ReactPackage>asList(
new MainReactPackage(mConfig){{ packageClassInstances }}
));
}
}
"""
.trimIndent()
}
}
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.model
data class ModelAutolinkingDependenciesJson(
val root: String,
val name: String,
+ val instantApp: Boolean?,
val platforms: ModelAutolinkingDependenciesPlatformJson?
) {
val nameCleansed: String
get() = name.replace(Regex("[~*!'()]+"), "_").replace(Regex("^@([\\w-.]+)/"), "$1_")
}
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react
import com.facebook.react.utils.JsonUtils
import com.facebook.react.utils.projectPathToLibraryName
import java.io.File
import javax.inject.Inject
import org.gradle.api.Project
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
abstract class ReactExtension @Inject constructor(val project: Project) {
private val objects = project.objects
/**
* The path to the root of your project. This is the path to where the `package.json` lives. All
* the CLI commands will be invoked from this folder as working directory.
*
* Default: ${rootProject.dir}/../
*/
val root: DirectoryProperty =
objects.directoryProperty().convention(project.rootProject.layout.projectDirectory.dir("../"))
/**
* The path to the react-native NPM package folder.
*
* Default: ${rootProject.dir}/../node_modules/react-native
*/
val reactNativeDir: DirectoryProperty =
objects.directoryProperty().convention(root.dir("node_modules/react-native"))
/**
* The path to the JS entry file. If not specified, the plugin will try to resolve it using a list
* of known locations (e.g. `index.android.js`, `index.js`, etc.).
*/
val entryFile: RegularFileProperty = objects.fileProperty()
/**
* The reference to the React Native CLI. If not specified, the plugin will try to resolve it
* looking for `react-native` CLI inside `node_modules` in [root].
*/
val cliFile: RegularFileProperty =
objects.fileProperty().convention(reactNativeDir.file("cli.js"))
/**
* The path to the Node executable and extra args. By default it assumes that you have `node`
* installed and configured in your $PATH. Default: ["node"]
*/
val nodeExecutableAndArgs: ListProperty<String> =
objects.listProperty(String::class.java).convention(listOf("node"))
/** The command to use to invoke bundle. Default is `bundle` and will be invoked on [root]. */
val bundleCommand: Property<String> = objects.property(String::class.java).convention("bundle")
/**
* Custom configuration file for the [bundleCommand]. If provided, it will be passed over with a
* `--config` flag to the bundle command.
*/
val bundleConfig: RegularFileProperty = objects.fileProperty()
/**
* The Bundle Asset name. This name will be used also for deriving other bundle outputs such as
* the packager source map, the compiler source map and the output source map file.
*
* Default: index.android.bundle
*/
val bundleAssetName: Property<String> =
objects.property(String::class.java).convention("index.android.bundle")
/**
* Toggles the .so Cleanup step. If enabled, we will clean up all the unnecessary files before the
* bundle task. If disabled, the developers will have to manually cleanup the files. Default: true
*/
val enableSoCleanup: Property<Boolean> = objects.property(Boolean::class.java).convention(true)
/** Extra args that will be passed to the [bundleCommand] Default: [] */
val extraPackagerArgs: ListProperty<String> =
objects.listProperty(String::class.java).convention(emptyList())
/**
* Allows to specify the debuggable variants (by default just 'debug'). Variants in this list will
* not be bundled (the bundle file will not be created and won't be copied over).
*
* Default: ['debug']
*/
val debuggableVariants: ListProperty<String> =
objects.listProperty(String::class.java).convention(listOf("debug"))
/** Hermes Config */
/**
* The command to use to invoke hermesc (the hermes compiler). Default is "", the plugin will
* autodetect it.
*/
val hermesCommand: Property<String> = objects.property(String::class.java).convention("")
/**
* Whether to enable Hermes only on certain variants. If specified as a non-empty list, hermesc
* and the .so cleanup for Hermes will be executed only for variants in this list. An empty list
* assumes you're either using Hermes for all variants or not (see [enableHermes]).
*
* Default: []
*/
val enableHermesOnlyInVariants: ListProperty<String> =
objects.listProperty(String::class.java).convention(emptyList())
/** Flags to pass to Hermesc. Default: ["-O", "-output-source-map"] */
val hermesFlags: ListProperty<String> =
objects.listProperty(String::class.java).convention(listOf("-O", "-output-source-map"))
/** Codegen Config */
/**
* The path to the react-native-codegen NPM package folder.
*
* Default: ${rootProject.dir}/../node_modules/@react-native/codegen
*/
val codegenDir: DirectoryProperty =
objects.directoryProperty().convention(root.dir("node_modules/@react-native/codegen"))
/**
* The root directory for all JS files for the app.
*
* Default: the parent folder of the `/android` folder.
*/
val jsRootDir: DirectoryProperty = objects.directoryProperty()
/**
* The library name that will be used for the codegen artifacts.
*
* Default: <UpperCamelVersionOfProjectPath>Spec (e.g. for :example:project it will be
* ExampleProjectSpec).
*/
val libraryName: Property<String> =
objects.property(String::class.java).convention(projectPathToLibraryName(project.path))
/**
* Java package name to use for any codegen artifacts produced during build time. Default:
* com.facebook.fbreact.specs
*/
val codegenJavaPackageName: Property<String> =
objects.property(String::class.java).convention("com.facebook.fbreact.specs")
+ /**
+ * It will be used determine app is instant app or not
+ */
+ var instantApp: Boolean = false
/** Auto-linking Utils */
/**
* Utility function to autolink libraries to the app.
*
* This function will read the autolinking configuration file and add Gradle dependencies to the
* app. This function should be invoked inside the react {} block in the app's build.gradle and is
* necessary for libraries to be linked correctly.
+ *
+ * @param instantApp Instant app activated or not
*/
- fun autolinkLibrariesWithApp() {
+ fun autolinkLibrariesWithApp(instantApp: Boolean = false) {
val inputFile =
project.rootProject.layout.buildDirectory
.file("generated/autolinking/autolinking.json")
.get()
.asFile
- val dependenciesToApply = getGradleDependenciesToApply(inputFile)
+ val dependenciesToApply = getGradleDependenciesToApply(inputFile, instantApp)
dependenciesToApply.forEach { (configuration, path) ->
project.dependencies.add(configuration, project.dependencies.project(mapOf("path" to path)))
}
}
companion object {
/**
* Util function to construct a list of Gradle Configuration <-> Project name pairs for
* autolinking. Pairs looks like: "implementation" -> ":react-native_oss-library-example"
*
* They will be applied to the Gradle project for linking the libraries.
*
* @param inputFile The file to read the autolinking configuration from.
+ * @param instantApp Instant app activated or not
* @return A list of Gradle Configuration <-> Project name pairs.
*/
- internal fun getGradleDependenciesToApply(inputFile: File): MutableList<Pair<String, String>> {
+ internal fun getGradleDependenciesToApply(inputFile: File, instantApp: Boolean): MutableList<Pair<String, String>> {
val model = JsonUtils.fromAutolinkingConfigJson(inputFile)
val result = mutableListOf<Pair<String, String>>()
model
?.dependencies
?.values
?.filter { it.platforms?.android !== null }
+ ?.filter { if (instantApp) it?.instantApp !== false else true }
?.filterNot { it.platforms?.android?.isPureCxxDependency == true }
?.forEach { deps ->
val nameCleansed = deps.nameCleansed
val dependencyConfiguration = deps.platforms?.android?.dependencyConfiguration
val buildTypes = deps.platforms?.android?.buildTypes ?: emptyList()
if (buildTypes.isEmpty()) {
result.add((dependencyConfiguration ?: "implementation") to ":$nameCleansed")
} else {
buildTypes.forEach { buildType ->
result.add(
(dependencyConfiguration ?: "${buildType}Implementation") to ":$nameCleansed")
}
}
}
return result
}
}
}
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react
import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.gradle.internal.tasks.factory.dependsOn
import com.facebook.react.internal.PrivateReactExtension
import com.facebook.react.tasks.GenerateAutolinkingNewArchitecturesFileTask
import com.facebook.react.tasks.GenerateCodegenArtifactsTask
import com.facebook.react.tasks.GenerateCodegenSchemaTask
import com.facebook.react.tasks.GeneratePackageListTask
import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFieldsForApp
import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFieldsForLibraries
import com.facebook.react.utils.AgpConfiguratorUtils.configureDevPorts
import com.facebook.react.utils.AgpConfiguratorUtils.configureNamespaceForLibraries
import com.facebook.react.utils.BackwardCompatUtils.configureBackwardCompatibilityReactMap
import com.facebook.react.utils.DependencyUtils.configureDependencies
import com.facebook.react.utils.DependencyUtils.configureRepositories
import com.facebook.react.utils.DependencyUtils.readVersionAndGroupStrings
import com.facebook.react.utils.JdkConfiguratorUtils.configureJavaToolChains
import com.facebook.react.utils.JsonUtils
import com.facebook.react.utils.NdkConfiguratorUtils.configureReactNativeNdk
import com.facebook.react.utils.ProjectUtils.isNewArchEnabled
import com.facebook.react.utils.ProjectUtils.needsCodegenFromPackageJson
import com.facebook.react.utils.ProjectUtils.shouldWarnIfNewArchFlagIsSetInPrealpha
import com.facebook.react.utils.findPackageJsonFile
import java.io.File
import kotlin.system.exitProcess
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.file.Directory
import org.gradle.api.provider.Provider
import org.gradle.internal.jvm.Jvm
class ReactPlugin : Plugin<Project> {
override fun apply(project: Project) {
checkJvmVersion(project)
val extension = project.extensions.create("react", ReactExtension::class.java, project)
checkIfNewArchFlagIsSet(project, extension)
// We register a private extension on the rootProject so that project wide configs
// like codegen config can be propagated from app project to libraries.
val rootExtension =
project.rootProject.extensions.findByType(PrivateReactExtension::class.java)
?: project.rootProject.extensions.create(
"privateReact", PrivateReactExtension::class.java, project)
// App Only Configuration
project.pluginManager.withPlugin("com.android.application") {
// We wire the root extension with the values coming from the app (either user populated or
// defaults).
rootExtension.root.set(extension.root)
rootExtension.reactNativeDir.set(extension.reactNativeDir)
rootExtension.codegenDir.set(extension.codegenDir)
rootExtension.nodeExecutableAndArgs.set(extension.nodeExecutableAndArgs)
project.afterEvaluate {
val reactNativeDir = extension.reactNativeDir.get().asFile
val propertiesFile = File(reactNativeDir, "ReactAndroid/gradle.properties")
val versionAndGroupStrings = readVersionAndGroupStrings(propertiesFile)
val versionString = versionAndGroupStrings.first
val groupString = versionAndGroupStrings.second
configureDependencies(project, versionString, groupString)
configureRepositories(project, reactNativeDir)
}
configureReactNativeNdk(project, extension)
configureBuildConfigFieldsForApp(project, extension)
configureDevPorts(project)
configureBackwardCompatibilityReactMap(project)
configureJavaToolChains(project)
project.extensions.getByType(AndroidComponentsExtension::class.java).apply {
onVariants(selector().all()) { variant ->
project.configureReactTasks(variant = variant, config = extension)
}
}
configureAutolinking(project, extension)
configureCodegen(project, extension, rootExtension, isLibrary = false)
}
// Library Only Configuration
configureBuildConfigFieldsForLibraries(project)
configureNamespaceForLibraries(project)
project.pluginManager.withPlugin("com.android.library") {
configureCodegen(project, extension, rootExtension, isLibrary = true)
}
}
private fun checkJvmVersion(project: Project) {
val jvmVersion = Jvm.current()?.javaVersion?.majorVersion
if ((jvmVersion?.toIntOrNull() ?: 0) <= 16) {
project.logger.error(
"""
********************************************************************************
ERROR: requires JDK17 or higher.
Incompatible major version detected: '$jvmVersion'
********************************************************************************
"""
.trimIndent())
exitProcess(1)
}
}
private fun checkIfNewArchFlagIsSet(project: Project, extension: ReactExtension) {
if (project.shouldWarnIfNewArchFlagIsSetInPrealpha(extension)) {
project.logger.warn(
"""
********************************************************************************
WARNING: This version of React Native is ignoring the `newArchEnabled` flag you set. Please set it to true or remove it to suppress this warning.
********************************************************************************
"""
.trimIndent())
}
}
/** This function sets up `react-native-codegen` in our Gradle plugin. */
@Suppress("UnstableApiUsage")
private fun configureCodegen(
project: Project,
localExtension: ReactExtension,
rootExtension: PrivateReactExtension,
isLibrary: Boolean
) {
// First, we set up the output dir for the codegen.
val generatedSrcDir: Provider<Directory> =
project.layout.buildDirectory.dir("generated/source/codegen")
// We specify the default value (convention) for jsRootDir.
// It's the root folder for apps (so ../../ from the Gradle project)
// and the package folder for library (so ../ from the Gradle project)
if (isLibrary) {
localExtension.jsRootDir.convention(project.layout.projectDirectory.dir("../"))
} else {
localExtension.jsRootDir.convention(localExtension.root)
}
// We create the task to produce schema from JS files.
val generateCodegenSchemaTask =
project.tasks.register(
"generateCodegenSchemaFromJavaScript", GenerateCodegenSchemaTask::class.java) { it ->
it.nodeExecutableAndArgs.set(rootExtension.nodeExecutableAndArgs)
it.codegenDir.set(rootExtension.codegenDir)
it.generatedSrcDir.set(generatedSrcDir)
// We're reading the package.json at configuration time to properly feed
// the `jsRootDir` @Input property of this task & the onlyIf. Therefore, the
// parsePackageJson should be invoked inside this lambda.
val packageJson = findPackageJsonFile(project, rootExtension.root)
val parsedPackageJson = packageJson?.let { JsonUtils.fromPackageJson(it) }
val jsSrcsDirInPackageJson = parsedPackageJson?.codegenConfig?.jsSrcsDir
val includesGeneratedCode =
parsedPackageJson?.codegenConfig?.includesGeneratedCode ?: false
if (jsSrcsDirInPackageJson != null) {
it.jsRootDir.set(File(packageJson.parentFile, jsSrcsDirInPackageJson))
} else {
it.jsRootDir.set(localExtension.jsRootDir)
}
val needsCodegenFromPackageJson =
project.needsCodegenFromPackageJson(rootExtension.root)
it.onlyIf { (isLibrary || needsCodegenFromPackageJson) && !includesGeneratedCode }
}
// We create the task to generate Java code from schema.
val generateCodegenArtifactsTask =
project.tasks.register(
"generateCodegenArtifactsFromSchema", GenerateCodegenArtifactsTask::class.java) {
it.dependsOn(generateCodegenSchemaTask)
it.reactNativeDir.set(rootExtension.reactNativeDir)
it.nodeExecutableAndArgs.set(rootExtension.nodeExecutableAndArgs)
it.generatedSrcDir.set(generatedSrcDir)
it.packageJsonFile.set(findPackageJsonFile(project, rootExtension.root))
it.codegenJavaPackageName.set(localExtension.codegenJavaPackageName)
it.libraryName.set(localExtension.libraryName)
// Please note that appNeedsCodegen is triggering a read of the package.json at
// configuration time as we need to feed the onlyIf condition of this task.
// Therefore, the appNeedsCodegen needs to be invoked inside this lambda.
val needsCodegenFromPackageJson =
project.needsCodegenFromPackageJson(rootExtension.root)
val packageJson = findPackageJsonFile(project, rootExtension.root)
val parsedPackageJson = packageJson?.let { JsonUtils.fromPackageJson(it) }
val includesGeneratedCode =
parsedPackageJson?.codegenConfig?.includesGeneratedCode ?: false
it.onlyIf { (isLibrary || needsCodegenFromPackageJson) && !includesGeneratedCode }
}
// We update the android configuration to include the generated sources.
// This equivalent to this DSL:
//
// android { sourceSets { main { java { srcDirs += "$generatedSrcDir/java" } } } }
project.extensions.getByType(AndroidComponentsExtension::class.java).finalizeDsl { ext ->
ext.sourceSets.getByName("main").java.srcDir(generatedSrcDir.get().dir("java").asFile)
}
// `preBuild` is one of the base tasks automatically registered by AGP.
// This will invoke the codegen before compiling the entire project.
project.tasks.named("preBuild", Task::class.java).dependsOn(generateCodegenArtifactsTask)
}
/** This function sets up Autolinking for App users */
private fun configureAutolinking(
project: Project,
extension: ReactExtension,
) {
val generatedAutolinkingJavaDir: Provider<Directory> =
project.layout.buildDirectory.dir("generated/autolinking/src/main/java")
val generatedAutolinkingJniDir: Provider<Directory> =
project.layout.buildDirectory.dir("generated/autolinking/src/main/jni")
// The autolinking.json file is available in the root build folder as it's generated
// by ReactSettingsPlugin.kt
val rootGeneratedAutolinkingFile =
project.rootProject.layout.buildDirectory.file("generated/autolinking/autolinking.json")
// We add a task called generateAutolinkingPackageList to do not clash with the existing task
// called generatePackageList. This can to be renamed once we unlink the rn <-> cli
// dependency.
val generatePackageListTask =
project.tasks.register(
"generateAutolinkingPackageList", GeneratePackageListTask::class.java) { task ->
task.autolinkInputFile.set(rootGeneratedAutolinkingFile)
task.generatedOutputDirectory.set(generatedAutolinkingJavaDir)
+ task.instantApp = extension.instantApp
}
if (project.isNewArchEnabled(extension)) {
// For New Arch, we also need to generate code for C++ Autolinking
val generateAutolinkingNewArchitectureFilesTask =
project.tasks.register(
"generateAutolinkingNewArchitectureFiles",
GenerateAutolinkingNewArchitecturesFileTask::class.java) { task ->
task.autolinkInputFile.set(rootGeneratedAutolinkingFile)
task.generatedOutputDirectory.set(generatedAutolinkingJniDir)
}
project.tasks
.named("preBuild", Task::class.java)
.dependsOn(generateAutolinkingNewArchitectureFilesTask)
}
// We let generateAutolinkingPackageList depend on the preBuild task so it's executed before
// everything else.
project.tasks.named("preBuild", Task::class.java).dependsOn(generatePackageListTask)
// We tell Android Gradle Plugin that inside /build/generated/autolinking/src/main/java there
// are sources to be compiled as well.
project.extensions.getByType(AndroidComponentsExtension::class.java).apply {
onVariants(selector().all()) { variant ->
variant.sources.java?.addStaticSourceDirectory(
generatedAutolinkingJavaDir.get().asFile.absolutePath)
}
}
}
}
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true,
});
exports.projectConfig = exports.dependencyConfig = void 0;
function _joi() {
const data = _interopRequireDefault(require('joi'));
_joi = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {default: obj};
}
/**
* This schema is used by `cli-config` to validate the structure. Make sure
* this file stays up to date with `cli-types` package.
*
* In the future, it would be great to generate this file automatically from the
* Typescript types.
*/
const map = (key, value) =>
_joi().default.object().unknown(true).pattern(key, value);
/**
* Schema for CommandT
*/
const command = _joi().default.object({
name: _joi().default.string().required(),
description: _joi().default.string(),
usage: _joi().default.string(),
func: _joi().default.func().required(),
options: _joi()
.default.array()
.items(
_joi()
.default.object({
name: _joi().default.string().required(),
description: _joi().default.string(),
parse: _joi().default.func(),
default: _joi()
.default.alternatives()
.try(
_joi().default.bool(),
_joi().default.number(),
_joi().default.string().allow(''),
_joi().default.func(),
),
})
.rename('command', 'name', {
ignoreUndefined: true,
}),
),
examples: _joi()
.default.array()
.items(
_joi().default.object({
desc: _joi().default.string().required(),
cmd: _joi().default.string().required(),
}),
),
});
/**
* Schema for HealthChecksT
*/
const healthCheck = _joi().default.object({
label: _joi().default.string().required(),
healthchecks: _joi()
.default.array()
.items(
_joi().default.object({
label: _joi().default.string().required(),
isRequired: _joi().default.bool(),
description: _joi().default.string(),
getDiagnostics: _joi().default.func(),
win32AutomaticFix: _joi().default.func(),
darwinAutomaticFix: _joi().default.func(),
linuxAutomaticFix: _joi().default.func(),
runAutomaticFix: _joi().default.func().required(),
}),
),
});
/**
* Schema for UserDependencyConfig
*/
const dependencyConfig = _joi()
.default.object({
dependency: _joi()
.default.object({
platforms: map(_joi().default.string(), _joi().default.any())
.keys({
ios: _joi()
.default // IOSDependencyParams
.object({
scriptPhases: _joi()
.default.array()
.items(_joi().default.object()),
configurations: _joi()
.default.array()
.items(_joi().default.string())
.default([]),
})
.allow(null),
android: _joi()
.default // AndroidDependencyParams
.object({
sourceDir: _joi().default.string(),
manifestPath: _joi().default.string(),
packageName: _joi().default.string(),
packageImportPath: _joi().default.string(),
packageInstance: _joi().default.string(),
dependencyConfiguration: _joi().default.string(),
buildTypes: _joi()
.default.array()
.items(_joi().default.string())
.default([]),
libraryName: _joi().default.string().allow(null),
componentDescriptors: _joi()
.default.array()
.items(_joi().default.string())
.allow(null),
cmakeListsPath: _joi().default.string().allow(null),
cxxModuleCMakeListsModuleName: _joi()
.default.string()
.allow(null),
cxxModuleCMakeListsPath: _joi().default.string().allow(null),
cxxModuleHeaderName: _joi().default.string().allow(null),
})
.allow(null),
})
.default(),
})
.default(),
platforms: map(
_joi().default.string(),
_joi().default.object({
npmPackageName: _joi().default.string().optional(),
dependencyConfig: _joi().default.func(),
projectConfig: _joi().default.func(),
linkConfig: _joi().default.func(),
}),
).default({}),
commands: _joi().default.array().items(command).default([]),
healthChecks: _joi().default.array().items(healthCheck).default([]),
})
.unknown(true)
.default();
/**
* Schema for ProjectConfig
*/
exports.dependencyConfig = dependencyConfig;
const projectConfig = _joi()
.default.object({
dependencies: map(
_joi().default.string(),
_joi()
.default.object({
root: _joi().default.string(),
+ instantApp: _joi().default.bool(),
platforms: map(_joi().default.string(), _joi().default.any()).keys({
ios: _joi()
.default // IOSDependencyConfig
.object({
podspecPath: _joi().default.string(),
version: _joi().default.string(),
configurations: _joi()
.default.array()
.items(_joi().default.string())
.default([]),
scriptPhases: _joi()
.default.array()
.items(_joi().default.object())
.default([]),
})
.allow(null),
android: _joi()
.default // AndroidDependencyConfig
.object({
sourceDir: _joi().default.string(),
packageImportPath: _joi().default.string(),
packageInstance: _joi().default.string(),
dependencyConfiguration: _joi().default.string(),
buildTypes: _joi()
.default.array()
.items(_joi().default.string())
.default([]),
libraryName: _joi().default.string().allow(null),
componentDescriptors: _joi()
.default.array()
.items(_joi().default.string())
.allow(null),
cmakeListsPath: _joi().default.string().allow(null),
})
.allow(null),
}),
})
.allow(null),
).default({}),
reactNativePath: _joi().default.string(),
project: map(_joi().default.string(), _joi().default.any())
.keys({
ios: _joi()
.default // IOSProjectParams
.object({
sourceDir: _joi().default.string(),
watchModeCommandParams: _joi()
.default.array()
.items(_joi().default.string()),
// @todo remove for RN 0.75
unstable_reactLegacyComponentNames: _joi()
.default.array()
.items(_joi().default.string())
.optional(),
automaticPodsInstallation: _joi().default.bool().default(false),
assets: _joi()
.default.array()
.items(_joi().default.string())
.default([]),
})
.default({}),
android: _joi()
.default // AndroidProjectParams
.object({
sourceDir: _joi().default.string(),
appName: _joi().default.string(),
manifestPath: _joi().default.string(),
packageName: _joi().default.string(),
dependencyConfiguration: _joi().default.string(),
watchModeCommandParams: _joi()
.default.array()
.items(_joi().default.string()),
// @todo remove for RN 0.75
unstable_reactLegacyComponentNames: _joi()
.default.array()
.items(_joi().default.string())
.optional(),
assets: _joi()
.default.array()
.items(_joi().default.string())
.default([]),
})
.default({}),
})
.default(),
assets: _joi().default.array().items(_joi().default.string()).default([]),
commands: _joi().default.array().items(command).default([]),
platforms: map(
_joi().default.string(),
_joi().default.object({
npmPackageName: _joi().default.string().optional(),
dependencyConfig: _joi().default.func(),
projectConfig: _joi().default.func(),
linkConfig: _joi().default.func(),
}),
).default({}),
})
.unknown(true)
.default();
exports.projectConfig = projectConfig;
//# sourceMappingURL=/Users/thymikee/Developer/oss/rncli/packages/cli-config/build/schema.js.map
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment