Skip to content

Commit 5f3c5aa

Browse files
cortinicofacebook-github-bot
authored andcommitted
Extend the React Native Gradle plugin to accept a config from package.json
Summary: This extends the Gradle plugin to allow configuration for `codegenConfig` from the `package.json` that lives in one of the root folder. There are a couple of points open for discussion. The most important one is that now we're moving from absolute paths to relative paths, from the package.json location. I'm not entirely sure this will work correctly for users in monorepos, so we might consider this carefully. Moreover, I've moved the `codegenJavaPackageName` to be `android.javaPackageName`. Happy to discuss this further. Changelog: [Android] [Added] - Extend the React Native Gradle plugin to accept a config from package.json Reviewed By: cipolleschi Differential Revision: D36374475 fbshipit-source-id: fe669ebd5bc92abbbe57677c1995d0e01f2400d7
1 parent 406a474 commit 5f3c5aa

File tree

14 files changed

+298
-28
lines changed

14 files changed

+298
-28
lines changed

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

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import com.android.build.gradle.internal.tasks.factory.dependsOn
1414
import com.facebook.react.tasks.BuildCodegenCLITask
1515
import com.facebook.react.tasks.GenerateCodegenArtifactsTask
1616
import com.facebook.react.tasks.GenerateCodegenSchemaTask
17+
import com.facebook.react.utils.JsonUtils
1718
import java.io.File
1819
import kotlin.system.exitProcess
1920
import org.gradle.api.Plugin
@@ -67,10 +68,10 @@ class ReactPlugin : Plugin<Project> {
6768

6869
/**
6970
* A plugin to enable react-native-codegen in Gradle environment. See the Gradle API docs for more
70-
* information: https://docs.gradle.org/6.5.1/javadoc/org/gradle/api/Project.html
71+
* information: https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html
7172
*/
7273
private fun applyCodegenPlugin(project: Project, extension: ReactExtension) {
73-
// 1. Set up build dir.
74+
// First, we set up the output dir for the codegen.
7475
val generatedSrcDir = File(project.buildDir, "generated/source/codegen")
7576

7677
val buildCodegenTask =
@@ -80,18 +81,33 @@ class ReactPlugin : Plugin<Project> {
8081
it.bashWindowsHome.set(bashWindowsHome)
8182
}
8283

83-
// 2. Task: produce schema from JS files.
84+
// We create the task to produce schema from JS files.
8485
val generateCodegenSchemaTask =
8586
project.tasks.register(
86-
"generateCodegenSchemaFromJavaScript", GenerateCodegenSchemaTask::class.java) {
87+
"generateCodegenSchemaFromJavaScript", GenerateCodegenSchemaTask::class.java) { it ->
8788
it.dependsOn(buildCodegenTask)
88-
it.jsRootDir.set(extension.jsRootDir)
8989
it.nodeExecutableAndArgs.set(extension.nodeExecutableAndArgs)
9090
it.codegenDir.set(extension.codegenDir)
9191
it.generatedSrcDir.set(generatedSrcDir)
92+
93+
// We're reading the package.json at configuration time to properly feed
94+
// the `jsRootDir` @Input property of this task. Therefore, the
95+
// parsePackageJson should be invoked here.
96+
val parsedPackageJson =
97+
extension.root.file("package.json").orNull?.asFile?.let {
98+
JsonUtils.fromCodegenJson(it)
99+
}
100+
101+
val parsedJsRootDir =
102+
parsedPackageJson?.codegenConfig?.jsSrcsDir?.let { relativePath ->
103+
extension.root.dir(relativePath)
104+
}
105+
?: extension.jsRootDir
106+
107+
it.jsRootDir.set(parsedJsRootDir)
92108
}
93109

94-
// 3. Task: generate Java code from schema.
110+
// We create the task to generate Java code from schema.
95111
val generateCodegenArtifactsTask =
96112
project.tasks.register(
97113
"generateCodegenArtifactsFromSchema", GenerateCodegenArtifactsTask::class.java) {
@@ -100,12 +116,13 @@ class ReactPlugin : Plugin<Project> {
100116
it.deprecatedReactRoot.set(extension.reactRoot)
101117
it.nodeExecutableAndArgs.set(extension.nodeExecutableAndArgs)
102118
it.codegenDir.set(extension.codegenDir)
119+
it.generatedSrcDir.set(generatedSrcDir)
120+
it.packageJsonFile.set(extension.root.file("package.json"))
103121
it.codegenJavaPackageName.set(extension.codegenJavaPackageName)
104122
it.libraryName.set(extension.libraryName)
105-
it.generatedSrcDir.set(generatedSrcDir)
106123
}
107124

108-
// 4. Add dependencies & generated sources to the project.
125+
// We add dependencies & generated sources to the project.
109126
// Note: This last step needs to happen after the project has been evaluated.
110127
project.afterEvaluate {
111128

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.react.model
9+
10+
data class ModelCodegenConfig(
11+
val name: String?,
12+
val type: String?,
13+
val jsSrcsDir: String?,
14+
val android: ModelCodegenConfigAndroid?
15+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.react.model
9+
10+
data class ModelCodegenConfigAndroid(val javaPackageName: String?)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.react.model
9+
10+
data class ModelPackageJson(val codegenConfig: ModelCodegenConfig?)

packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GenerateCodegenArtifactsTask.kt

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,20 @@
77

88
package com.facebook.react.tasks
99

10+
import com.facebook.react.utils.JsonUtils
1011
import com.facebook.react.utils.windowsAwareCommandLine
1112
import org.gradle.api.file.Directory
1213
import org.gradle.api.file.DirectoryProperty
1314
import org.gradle.api.file.RegularFile
15+
import org.gradle.api.file.RegularFileProperty
1416
import org.gradle.api.provider.ListProperty
1517
import org.gradle.api.provider.Property
1618
import org.gradle.api.provider.Provider
17-
import org.gradle.api.tasks.*
19+
import org.gradle.api.tasks.Exec
20+
import org.gradle.api.tasks.Input
21+
import org.gradle.api.tasks.InputFile
22+
import org.gradle.api.tasks.Internal
23+
import org.gradle.api.tasks.OutputDirectory
1824

1925
abstract class GenerateCodegenArtifactsTask : Exec() {
2026

@@ -24,6 +30,8 @@ abstract class GenerateCodegenArtifactsTask : Exec() {
2430

2531
@get:Internal abstract val generatedSrcDir: DirectoryProperty
2632

33+
@get:InputFile abstract val packageJsonFile: RegularFileProperty
34+
2735
@get:Input abstract val nodeExecutableAndArgs: ListProperty<String>
2836

2937
@get:Input abstract val codegenJavaPackageName: Property<String>
@@ -46,7 +54,9 @@ abstract class GenerateCodegenArtifactsTask : Exec() {
4654

4755
override fun exec() {
4856
checkForDeprecatedProperty()
49-
setupCommandLine()
57+
58+
val (resolvedLibraryName, resolvedCodegenJavaPackageName) = resolveTaskParameters()
59+
setupCommandLine(resolvedLibraryName, resolvedCodegenJavaPackageName)
5060
super.exec()
5161
}
5262

@@ -74,7 +84,20 @@ abstract class GenerateCodegenArtifactsTask : Exec() {
7484
}
7585
}
7686

77-
internal fun setupCommandLine() {
87+
internal fun resolveTaskParameters(): Pair<String, String> {
88+
val parsedPackageJson =
89+
if (packageJsonFile.isPresent && packageJsonFile.get().asFile.exists()) {
90+
JsonUtils.fromCodegenJson(packageJsonFile.get().asFile)
91+
} else {
92+
null
93+
}
94+
val resolvedLibraryName = parsedPackageJson?.codegenConfig?.name ?: libraryName.get()
95+
val resolvedCodegenJavaPackageName =
96+
parsedPackageJson?.codegenConfig?.android?.javaPackageName ?: codegenJavaPackageName.get()
97+
return resolvedLibraryName to resolvedCodegenJavaPackageName
98+
}
99+
100+
internal fun setupCommandLine(libraryName: String, codegenJavaPackageName: String) {
78101
commandLine(
79102
windowsAwareCommandLine(
80103
*nodeExecutableAndArgs.get().toTypedArray(),
@@ -86,8 +109,8 @@ abstract class GenerateCodegenArtifactsTask : Exec() {
86109
"--outputDir",
87110
generatedSrcDir.get().asFile.absolutePath,
88111
"--libraryName",
89-
libraryName.get(),
112+
libraryName,
90113
"--javaPackageName",
91-
codegenJavaPackageName.get()))
114+
codegenJavaPackageName))
92115
}
93116
}

packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GenerateCodegenSchemaTask.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,12 @@ abstract class GenerateCodegenSchemaTask : Exec() {
2929

3030
@get:Input abstract val nodeExecutableAndArgs: ListProperty<String>
3131

32-
@get:InputFiles val jsInputFiles = project.fileTree(jsRootDir) { it.include("**/*.js") }
32+
@get:InputFiles
33+
val jsInputFiles =
34+
project.fileTree(jsRootDir) {
35+
it.include("**/*.js")
36+
it.exclude("**/generated/source/codegen/**/*")
37+
}
3338

3439
@get:OutputFile
3540
val generatedSchemaFile: Provider<RegularFile> = generatedSrcDir.file("schema.json")
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.react.utils
9+
10+
import com.facebook.react.model.ModelPackageJson
11+
import com.google.gson.Gson
12+
import java.io.File
13+
14+
object JsonUtils {
15+
private val gsonConverter = Gson()
16+
17+
fun fromCodegenJson(input: File): ModelPackageJson? =
18+
input.bufferedReader().use {
19+
runCatching { gsonConverter.fromJson(it, ModelPackageJson::class.java) }.getOrNull()
20+
}
21+
}

packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/tasks/GenerateCodegenArtifactsTaskTest.kt

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,14 @@ class GenerateCodegenArtifactsTaskTest {
5858

5959
@Test
6060
fun generateCodegenSchema_simpleProperties_areInsideInput() {
61+
val packageJsonFile = tempFolder.newFile("package.json")
62+
6163
val task =
6264
createTestTask<GenerateCodegenArtifactsTask> {
6365
it.nodeExecutableAndArgs.set(listOf("npm", "help"))
6466
it.codegenJavaPackageName.set("com.example.test")
6567
it.libraryName.set("example-test")
68+
it.packageJsonFile.set(packageJsonFile)
6669
}
6770

6871
assertEquals(listOf("npm", "help"), task.nodeExecutableAndArgs.get())
@@ -75,7 +78,7 @@ class GenerateCodegenArtifactsTaskTest {
7578

7679
@Test
7780
@WithOs(OS.UNIX)
78-
fun setupCommandLine_withoutJavaGenerator_willSetupCorrectly() {
81+
fun setupCommandLine_willSetupCorrectly() {
7982
val reactNativeDir = tempFolder.newFolder("node_modules/react-native/")
8083
val codegenDir = tempFolder.newFolder("codegen")
8184
val outputDir = tempFolder.newFolder("output")
@@ -86,11 +89,9 @@ class GenerateCodegenArtifactsTaskTest {
8689
it.codegenDir.set(codegenDir)
8790
it.generatedSrcDir.set(outputDir)
8891
it.nodeExecutableAndArgs.set(listOf("--verbose"))
89-
it.codegenJavaPackageName.set("com.example.test")
90-
it.libraryName.set("example-test")
9192
}
9293

93-
task.setupCommandLine()
94+
task.setupCommandLine("example-test", "com.example.test")
9495

9596
assertEquals(
9697
listOf(
@@ -109,4 +110,79 @@ class GenerateCodegenArtifactsTaskTest {
109110
),
110111
task.commandLine.toMutableList())
111112
}
113+
114+
@Test
115+
fun resolveTaskParameters_withConfigInPackageJson_usesIt() {
116+
val packageJsonFile =
117+
tempFolder.newFile("package.json").apply {
118+
// language=JSON
119+
writeText(
120+
"""
121+
{
122+
"name": "@a/libray",
123+
"codegenConfig": {
124+
"name": "an-awesome-library",
125+
"android": {
126+
"javaPackageName": "com.awesome.package"
127+
}
128+
}
129+
}
130+
""".trimIndent())
131+
}
132+
133+
val task =
134+
createTestTask<GenerateCodegenArtifactsTask> {
135+
it.packageJsonFile.set(packageJsonFile)
136+
it.codegenJavaPackageName.set("com.example.ignored")
137+
it.libraryName.set("a-library-name-that-is-ignored")
138+
}
139+
140+
val (libraryName, javaPackageName) = task.resolveTaskParameters()
141+
142+
assertEquals("an-awesome-library", libraryName)
143+
assertEquals("com.awesome.package", javaPackageName)
144+
}
145+
146+
@Test
147+
fun resolveTaskParameters_withConfigMissingInPackageJson_usesGradleOne() {
148+
val packageJsonFile =
149+
tempFolder.newFile("package.json").apply {
150+
// language=JSON
151+
writeText(
152+
"""
153+
{
154+
"name": "@a/libray",
155+
"codegenConfig": {
156+
}
157+
}
158+
""".trimIndent())
159+
}
160+
161+
val task =
162+
createTestTask<GenerateCodegenArtifactsTask> {
163+
it.packageJsonFile.set(packageJsonFile)
164+
it.codegenJavaPackageName.set("com.example.test")
165+
it.libraryName.set("a-library-name-from-gradle")
166+
}
167+
168+
val (libraryName, javaPackageName) = task.resolveTaskParameters()
169+
170+
assertEquals("a-library-name-from-gradle", libraryName)
171+
assertEquals("com.example.test", javaPackageName)
172+
}
173+
174+
@Test
175+
fun resolveTaskParameters_withMissingPackageJson_usesGradleOne() {
176+
val task =
177+
createTestTask<GenerateCodegenArtifactsTask> {
178+
it.packageJsonFile.set(File(tempFolder.root, "package.json"))
179+
it.codegenJavaPackageName.set("com.example.test")
180+
it.libraryName.set("a-library-name-from-gradle")
181+
}
182+
183+
val (libraryName, javaPackageName) = task.resolveTaskParameters()
184+
185+
assertEquals("a-library-name-from-gradle", libraryName)
186+
assertEquals("com.example.test", javaPackageName)
187+
}
112188
}

0 commit comments

Comments
 (0)