Last active
August 26, 2025 07:19
-
-
Save ridvanaltun/5bf4e072e8db7a4ecd93770c542b1387 to your computer and use it in GitHub Desktop.
Revisions
-
ridvanaltun revised this gist
Oct 26, 2024 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -72,7 +72,7 @@ Go to file: https://gist.github.com/ridvanaltun/5bf4e072e8db7a4ecd93770c542b1387 Go to file: https://gist.github.com/ridvanaltun/5bf4e072e8db7a4ecd93770c542b1387#file-modelautolinkingdependenciesjson-diff 9. After all this changes we need patch all of them ```sh npx patch-package @react-native-community/cli-config -
ridvanaltun renamed this gist
Oct 25, 2024 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
ridvanaltun renamed this gist
Oct 25, 2024 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
ridvanaltun revised this gist
Oct 25, 2024 . 3 changed files with 6 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -98,7 +98,7 @@ abstract class GeneratePackageListTask : DefaultTask() { 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) } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -10,7 +10,7 @@ package com.facebook.react.model data class ModelAutolinkingDependenciesJson( val root: String, val name: String, + val instantApp: Boolean?, val platforms: ModelAutolinkingDependenciesPlatformJson? ) { val nameCleansed: String This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -166,12 +166,14 @@ abstract class ReactExtension @Inject constructor(val project: Project) { + * + * @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))) @@ -189,14 +191,15 @@ abstract class ReactExtension @Inject constructor(val project: Project) { + * @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 -
ridvanaltun revised this gist
Oct 25, 2024 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -6,7 +6,7 @@ My approach is keeping the autolinking mechanism and use `react-native.config.js Following changes are necessary: 1. Mark don't needed libraries with `instantApp` key in `react-native.config.js` file: ```js module.exports = { -
ridvanaltun revised this gist
Oct 25, 2024 . No changes.There are no files selected for viewing
-
ridvanaltun revised this gist
Oct 25, 2024 . 1 changed file with 10 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -46,22 +46,32 @@ react { `node_modules/@react-native-community/cli-config/build/schema.js` Go to file: https://gist.github.com/ridvanaltun/5bf4e072e8db7a4ecd93770c542b1387#file-schema-diff 5. 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 6. 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 7. 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 8. 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 After all this changes we need patch all of them ```sh -
ridvanaltun created this gist
Oct 25, 2024 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,206 @@ /* * 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 { instantApp && it.instantApp !== false } // 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() } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,18 @@ /* * 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_") } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,72 @@ 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 ```js module.exports = { dependencies: { ['react-native-camera']: { instantApp: false, }, }, }; ``` 2. Update instantApp/build.gradle file: ```gradle react { // ... instantApp = true /* Autolinking */ autolinkLibrariesWithApp(true) } ``` 3. Update app/build.gradle file: ```gradle react { // ... /* Autolinking */ autolinkLibrariesWithApp(false) } ``` 4. Update `@react-native-community/cli-config`: `node_modules/@react-native-community/cli-config/build/schema.js` 5. 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` 6. 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` 7. 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` 8. Update `ModelAutolinkingDependenciesJson.kt` in `@react-native/gradle-plugin`: `node_modules/@react-native/gradle-plugin/shared/src/main/kotlin/com/facebook/react/model/ModelAutolinkingDependenciesJson.kt` After all this changes we need patch all of them ```sh npx patch-package @react-native-community/cli-config npx patch-package @react-native/gradle-plugin ``` I think thats all. This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,217 @@ /* * 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(instantApp: Boolean = false) { val inputFile = project.rootProject.layout.buildDirectory .file("generated/autolinking/autolinking.json") .get() .asFile + 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, 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 { instantApp && it.instantApp !== false } ?.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 } } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,270 @@ /* * 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) } } } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,267 @@ '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