RNGP - Use the File Api to specify cliPath and remove ComposeSourceMap path (#35092)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/35092

As we're close to the cut of the 0.71 branch, I'll take the opportunity to polish
our Gradle public API.
We're exposing a mixture of Path (String) and File (java.io.File or RegularFileProperty).
Here I've moved everything to use File as it's more configurable for the users,
specifically if they're using monorepos or other setup.

This also allows us to remove the resolution logic for the cliPath.

Changelog:
[Internal] [Changed] - RNGP - Use the File Api to specify cliPath and remove ComposeSourceMap path

Reviewed By: cipolleschi

Differential Revision: D40710595

fbshipit-source-id: a17095eebae5123b70fd2b8e3d512656817006ca
This commit is contained in:
Nicola Corti
2022-10-26 13:05:03 -07:00
committed by Facebook GitHub Bot
parent 1069841837
commit 2df63e52f4
7 changed files with 78 additions and 146 deletions
@@ -28,6 +28,14 @@ abstract class ReactExtension @Inject constructor(project: Project) {
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-codegen
*/
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.).
@@ -35,10 +43,11 @@ abstract class ReactExtension @Inject constructor(project: Project) {
val entryFile: RegularFileProperty = objects.fileProperty()
/**
* The path 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].
* 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 cliPath: Property<String> = objects.property(String::class.java)
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`
@@ -107,15 +116,6 @@ abstract class ReactExtension @Inject constructor(project: Project) {
val hermesFlags: ListProperty<String> =
objects.listProperty(String::class.java).convention(listOf("-O", "-output-source-map"))
/**
* The path to the Compose Source Map script. Default:
* "node_modules/react-native/scripts/compose-source-maps.js"
*/
val composeSourceMapsPath: Property<String> =
objects
.property(String::class.java)
.convention("node_modules/react-native/scripts/compose-source-maps.js")
/** Codegen Config */
/**
@@ -126,14 +126,6 @@ abstract class ReactExtension @Inject constructor(project: Project) {
val codegenDir: DirectoryProperty =
objects.directoryProperty().convention(root.dir("node_modules/react-native-codegen"))
/**
* The path to the react-native NPM package folder.
*
* Default: ${rootProject.dir}/../node_modules/react-native-codegen
*/
val reactNativeDir: DirectoryProperty =
objects.directoryProperty().convention(root.dir("node_modules/react-native"))
/**
* The root directory for all JS files for the app.
*
@@ -12,7 +12,7 @@ import com.facebook.react.tasks.BundleHermesCTask
import com.facebook.react.utils.NdkConfiguratorUtils.configureJsEnginePackagingOptions
import com.facebook.react.utils.NdkConfiguratorUtils.configureNewArchPackagingOptions
import com.facebook.react.utils.ProjectUtils.isHermesEnabled
import com.facebook.react.utils.detectedCliPath
import com.facebook.react.utils.detectedCliFile
import com.facebook.react.utils.detectedEntryFile
import java.io.File
import org.gradle.api.Project
@@ -32,8 +32,8 @@ internal fun Project.configureReactTasks(variant: Variant, config: ReactExtensio
// Intermediate compiler: intermediates/sourcemaps/react/path/index.android.bundle.compiler.map
val jsIntermediateSourceMapsDir = File(buildDir, "intermediates/sourcemaps/react/$targetPath")
// Additional node and packager commandline arguments
val cliPath = detectedCliPath(project.projectDir, config)
// The location of the cli.js file for React Native
val cliFile = detectedCliFile(config)
val isHermesEnabledInProject = project.isHermesEnabled
val isHermesEnabledInThisVariant =
@@ -54,7 +54,7 @@ internal fun Project.configureReactTasks(variant: Variant, config: ReactExtensio
tasks.register("createBundle${targetName}JsAndAssets", BundleHermesCTask::class.java) {
it.root.set(config.root)
it.nodeExecutableAndArgs.set(config.nodeExecutableAndArgs)
it.cliPath.set(cliPath)
it.cliFile.set(cliFile)
it.bundleCommand.set(config.bundleCommand)
it.entryFile.set(detectedEntryFile(config))
it.extraPackagerArgs.set(config.extraPackagerArgs)
@@ -69,7 +69,7 @@ internal fun Project.configureReactTasks(variant: Variant, config: ReactExtensio
it.jsSourceMapsDir.set(jsSourceMapsDir)
it.hermesCommand.set(config.hermesCommand)
it.hermesFlags.set(config.hermesFlags)
it.composeSourceMapsPath.set(config.composeSourceMapsPath)
it.reactNativeDir.set(config.reactNativeDir)
}
variant.sources.res?.addGeneratedSourceDirectory(bundleTask, BundleHermesCTask::resourcesDir)
variant.sources.assets?.addGeneratedSourceDirectory(bundleTask, BundleHermesCTask::jsBundleDir)
@@ -42,9 +42,9 @@ abstract class BundleHermesCTask : DefaultTask() {
@get:Input abstract val nodeExecutableAndArgs: ListProperty<String>
@get:Input abstract val cliPath: Property<String>
@get:InputFile abstract val cliFile: RegularFileProperty
@get:Input abstract val composeSourceMapsPath: Property<String>
@get:Internal abstract val reactNativeDir: DirectoryProperty
@get:Input abstract val bundleCommand: Property<String>
@@ -101,8 +101,12 @@ abstract class BundleHermesCTask : DefaultTask() {
if (hermesFlags.get().contains("-output-source-map")) {
val hermesTempSourceMapFile = File("$bytecodeFile.map")
hermesTempSourceMapFile.moveTo(compilerSourceMap)
val reactNativeDir = reactNativeDir.get().asFile
val composeScriptFile = File(reactNativeDir, "scripts/compose-source-maps.js")
val composeSourceMapsCommand =
getComposeSourceMapsCommand(packagerSourceMap, compilerSourceMap, outputSourceMap)
getComposeSourceMapsCommand(
composeScriptFile, packagerSourceMap, compilerSourceMap, outputSourceMap)
runCommand(composeSourceMapsCommand)
}
}
@@ -132,7 +136,7 @@ abstract class BundleHermesCTask : DefaultTask() {
windowsAwareCommandLine(
buildList {
addAll(nodeExecutableAndArgs.get())
add(cliPath.get())
add(cliFile.get().asFile.absolutePath)
add(bundleCommand.get())
add("--platform")
add("android")
@@ -171,13 +175,14 @@ abstract class BundleHermesCTask : DefaultTask() {
*hermesFlags.get().toTypedArray())
internal fun getComposeSourceMapsCommand(
composeScript: File,
packagerSourceMap: File,
compilerSourceMap: File,
outputSourceMap: File
): List<Any> =
windowsAwareCommandLine(
*nodeExecutableAndArgs.get().toTypedArray(),
composeSourceMapsPath.get(),
composeScript.absolutePath,
packagerSourceMap.toString(),
compilerSourceMap.toString(),
"-o",
@@ -28,20 +28,16 @@ internal fun detectedEntryFile(config: ReactExtension): File =
entryFile = config.entryFile.orNull?.asFile, reactRoot = config.root.get().asFile)
/**
* Computes the CLI location for React Native. The Algo follows this order:
* 1. The path provided by the `cliPath` config in the `reactApp` Gradle extension
* Computes the CLI file for React Native. The Algo follows this order:
* 1. The path provided by the `cliFile` config in the `react {}` Gradle extension
* 2. The output of `node --print "require.resolve('react-native/cli');"` if not failing.
* 3. The `node_modules/react-native/cli.js` file if exists
* 4. Fails otherwise
*/
internal fun detectedCliPath(
projectDir: File,
config: ReactExtension,
): String =
detectCliPath(
projectDir = projectDir,
reactRoot = config.root.get().asFile,
preconfiguredCliPath = config.cliPath.orNull)
internal fun detectedCliFile(config: ReactExtension): File =
detectCliFile(
reactNativeRoot = config.root.get().asFile,
preconfiguredCliFile = config.cliFile.asFile.orNull)
/**
* Computes the `hermesc` command location. The Algo follows this order:
@@ -64,24 +60,11 @@ private fun detectEntryFile(entryFile: File?, reactRoot: File): File =
else -> File(reactRoot, "index.js")
}
private fun detectCliPath(
projectDir: File,
reactRoot: File,
preconfiguredCliPath: String?
): String {
private fun detectCliFile(reactNativeRoot: File, preconfiguredCliFile: File?): File {
// 1. preconfigured path
if (preconfiguredCliPath != null) {
val preconfiguredCliJsAbsolute = File(preconfiguredCliPath)
if (preconfiguredCliJsAbsolute.exists()) {
return preconfiguredCliJsAbsolute.absolutePath
}
val preconfiguredCliJsRelativeToReactRoot = File(reactRoot, preconfiguredCliPath)
if (preconfiguredCliJsRelativeToReactRoot.exists()) {
return preconfiguredCliJsRelativeToReactRoot.absolutePath
}
val preconfiguredCliJsRelativeToProject = File(projectDir, preconfiguredCliPath)
if (preconfiguredCliJsRelativeToProject.exists()) {
return preconfiguredCliJsRelativeToProject.absolutePath
if (preconfiguredCliFile != null) {
if (preconfiguredCliFile.exists()) {
return preconfiguredCliFile
}
}
@@ -91,27 +74,32 @@ private fun detectCliPath(
.exec(
arrayOf("node", "--print", "require.resolve('react-native/cli');"),
emptyArray(),
reactRoot)
reactNativeRoot)
val nodeProcessOutput = nodeProcess.inputStream.use { it.bufferedReader().readText().trim() }
if (nodeProcessOutput.isNotEmpty()) {
val nodeModuleCliJs = File(nodeProcessOutput)
if (nodeModuleCliJs.exists()) {
return nodeModuleCliJs.absolutePath
return nodeModuleCliJs
}
}
// 3. cli.js in the root folder
val rootCliJs = File(reactRoot, "node_modules/react-native/cli.js")
val rootCliJs = File(reactNativeRoot, "node_modules/react-native/cli.js")
if (rootCliJs.exists()) {
return rootCliJs.absolutePath
return rootCliJs
}
error(
"Couldn't determine CLI location. " +
"Please set `project.react.cliPath` to the path of the react-native cli.js file. " +
"This file typically resides in `node_modules/react-native/cli.js`")
"""
Couldn't determine CLI location!
Please set `react { cliFile = file(...) }` inside your
build.gradle to the path of the react-native cli.js file.
This file typically resides in `node_modules/react-native/cli.js`
"""
.trimIndent())
}
/**
@@ -85,9 +85,6 @@ class BundleHermesCTaskTest {
val task =
createTestTask<BundleHermesCTask> {
it.nodeExecutableAndArgs.set(listOf("node", "arg1", "arg2"))
it.cliPath.set("../node_modules/react-native/cli.js")
it.composeSourceMapsPath.set(
"../node_modules/react-native/scripts/compose-source-maps.js")
it.bundleCommand.set("bundle")
it.bundleAssetName.set("myassetname")
it.minifyEnabled.set(true)
@@ -99,10 +96,6 @@ class BundleHermesCTaskTest {
}
assertEquals(listOf("node", "arg1", "arg2"), task.nodeExecutableAndArgs.get())
assertEquals("../node_modules/react-native/cli.js", task.cliPath.get())
assertEquals(
"../node_modules/react-native/scripts/compose-source-maps.js",
task.composeSourceMapsPath.get())
assertEquals("bundle", task.bundleCommand.get())
assertEquals("myassetname", task.bundleAssetName.get())
assertTrue(task.minifyEnabled.get())
@@ -116,28 +109,34 @@ class BundleHermesCTaskTest {
@Test
fun bundleTask_filesInput_areSetCorrectly() {
val entryFile = tempFolder.newFile("entry.js")
val cliFile = tempFolder.newFile("cli.js")
val jsBundleDir = tempFolder.newFolder("jsbundle")
val resourcesDir = tempFolder.newFolder("resources")
val jsIntermediateSourceMapsDir = tempFolder.newFolder("jsIntermediateSourceMaps")
val jsSourceMapsDir = tempFolder.newFolder("jsSourceMaps")
val bundleConfig = tempFolder.newFile("bundle.config")
val reactNativeDir = tempFolder.newFolder("node_modules/react-native")
val task =
createTestTask<BundleHermesCTask> {
it.entryFile.set(entryFile)
it.cliFile.set(cliFile)
it.jsBundleDir.set(jsBundleDir)
it.resourcesDir.set(resourcesDir)
it.jsIntermediateSourceMapsDir.set(jsIntermediateSourceMapsDir)
it.jsSourceMapsDir.set(jsSourceMapsDir)
it.bundleConfig.set(bundleConfig)
it.reactNativeDir.set(reactNativeDir)
}
assertEquals(entryFile, task.entryFile.get().asFile)
assertEquals(cliFile, task.cliFile.get().asFile)
assertEquals(jsBundleDir, task.jsBundleDir.get().asFile)
assertEquals(resourcesDir, task.resourcesDir.get().asFile)
assertEquals(jsIntermediateSourceMapsDir, task.jsIntermediateSourceMapsDir.get().asFile)
assertEquals(jsSourceMapsDir, task.jsSourceMapsDir.get().asFile)
assertEquals(bundleConfig, task.bundleConfig.get().asFile)
assertEquals(reactNativeDir, task.reactNativeDir.get().asFile)
}
@Test
@@ -198,6 +197,7 @@ class BundleHermesCTaskTest {
@Test
fun getBundleCommand_returnsCorrectCommand() {
val entryFile = tempFolder.newFile("index.js")
val cliFile = tempFolder.newFile("cli.js")
val bundleFile = tempFolder.newFile("bundle.js")
val sourceMapFile = tempFolder.newFile("bundle.js.map")
val resourcesDir = tempFolder.newFolder("res")
@@ -205,7 +205,7 @@ class BundleHermesCTaskTest {
val task =
createTestTask<BundleHermesCTask> {
it.nodeExecutableAndArgs.set(listOf("node", "arg1", "arg2"))
it.cliPath.set("../node_modules/react-native/cli.js")
it.cliFile.set(cliFile)
it.bundleCommand.set("bundle")
it.devEnabled.set(true)
it.entryFile.set(entryFile)
@@ -220,7 +220,7 @@ class BundleHermesCTaskTest {
assertEquals("node", bundleCommand[0])
assertEquals("arg1", bundleCommand[1])
assertEquals("arg2", bundleCommand[2])
assertEquals("../node_modules/react-native/cli.js", bundleCommand[3])
assertEquals(cliFile.absolutePath, bundleCommand[3])
assertEquals("bundle", bundleCommand[4])
assertEquals("--platform", bundleCommand[5])
assertEquals("android", bundleCommand[6])
@@ -247,13 +247,14 @@ class BundleHermesCTaskTest {
@Test
fun getBundleCommand_withoutConfig_returnsCommandWithoutConfig() {
val entryFile = tempFolder.newFile("index.js")
val cliFile = tempFolder.newFile("cli.js")
val bundleFile = tempFolder.newFile("bundle.js")
val sourceMapFile = tempFolder.newFile("bundle.js.map")
val resourcesDir = tempFolder.newFolder("res")
val task =
createTestTask<BundleHermesCTask> {
it.nodeExecutableAndArgs.set(listOf("node", "arg1", "arg2"))
it.cliPath.set("../node_modules/react-native/cli.js")
it.cliFile.set(cliFile)
it.bundleCommand.set("bundle")
it.devEnabled.set(true)
it.entryFile.set(entryFile)
@@ -291,21 +292,20 @@ class BundleHermesCTaskTest {
val packagerMap = tempFolder.newFile("bundle.js.packager.map")
val compilerMap = tempFolder.newFile("bundle.js.compiler.map")
val outputMap = tempFolder.newFile("bundle.js.map")
val reactNativeDir = tempFolder.newFolder("node_modules/react-native")
val composeSourceMapsFile = File(reactNativeDir, "scripts/compose-source-maps.js")
val task =
createTestTask<BundleHermesCTask> {
it.nodeExecutableAndArgs.set(listOf("node", "arg1", "arg2"))
it.composeSourceMapsPath.set(
"../node_modules/react-native/scripts/compose-source-maps.js")
}
val composeSourcemapCommand =
task.getComposeSourceMapsCommand(packagerMap, compilerMap, outputMap)
task.getComposeSourceMapsCommand(composeSourceMapsFile, packagerMap, compilerMap, outputMap)
assertEquals("node", composeSourcemapCommand[0])
assertEquals("arg1", composeSourcemapCommand[1])
assertEquals("arg2", composeSourcemapCommand[2])
assertEquals(
"../node_modules/react-native/scripts/compose-source-maps.js", composeSourcemapCommand[3])
assertEquals(composeSourceMapsFile.absolutePath, composeSourcemapCommand[3])
assertEquals(packagerMap.absolutePath, composeSourcemapCommand[4])
assertEquals(compilerMap.absolutePath, composeSourcemapCommand[5])
assertEquals("-o", composeSourcemapCommand[6])
@@ -59,83 +59,31 @@ class PathUtilsTest {
}
@Test
fun detectedCliPath_withCliPathFromExtensionAbsolute() {
fun detectedCliPath_withCliPathFromExtensionAndFileExists_returnsIt() {
val project = ProjectBuilder.builder().build()
val cliFile = tempFolder.newFile("cli.js").apply { createNewFile() }
val extension = TestReactExtension(project)
val expected =
File(project.projectDir, "abs/fake-cli.sh").apply {
parentFile.mkdirs()
writeText("<!-- nothing to see here -->")
}
extension.cliPath.set(project.projectDir.toString() + "/abs/fake-cli.sh")
extension.cliFile.set(cliFile)
val actual = detectedCliPath(project.projectDir, extension)
val actual = detectedCliFile(extension)
assertEquals(expected.toString(), actual)
}
@Test
fun detectedCliPath_withCliPathFromExtensionInReactFolder() {
val project = ProjectBuilder.builder().build()
val extension = TestReactExtension(project)
val expected =
File(project.projectDir, "/react-root/fake-cli.sh").apply {
parentFile.mkdirs()
writeText("<!-- nothing to see here -->")
}
extension.cliPath.set("fake-cli.sh")
extension.root.set(File(project.projectDir.toString(), "react-root"))
val actual = detectedCliPath(project.projectDir, extension)
assertEquals(expected.toString(), actual)
}
@Test
fun detectedCliPath_withCliPathFromExtensionInProjectFolder() {
val project = ProjectBuilder.builder().build()
val extension = TestReactExtension(project)
val expected =
File(project.projectDir, "fake-cli.sh").apply {
parentFile.mkdirs()
writeText("<!-- nothing to see here -->")
}
extension.cliPath.set("fake-cli.sh")
val actual = detectedCliPath(project.projectDir, extension)
assertEquals(expected.toString(), actual)
}
@Test
fun detectedCliPath_withCliPathFromExtensionInParentFolder() {
val rootProject = ProjectBuilder.builder().build()
val project = ProjectBuilder.builder().withParent(rootProject).build()
project.projectDir.mkdirs()
val extension = TestReactExtension(project)
val expected = File(rootProject.projectDir, "cli-in-root.sh").apply { writeText("#!/bin/bash") }
extension.cliPath.set("../cli-in-root.sh")
val actual = detectedCliPath(project.projectDir, extension)
assertEquals(expected.canonicalPath, File(actual).canonicalPath)
assertEquals(cliFile, actual)
}
@Test
fun detectedCliPath_withCliFromNodeModules() {
val project = ProjectBuilder.builder().build()
val extension = TestReactExtension(project)
val expected =
File(tempFolder.root, "node_modules/react-native/cli.js").apply {
parentFile.mkdirs()
writeText("<!-- nothing to see here -->")
}
File(tempFolder.root, "node_modules/react-native/cli.js").apply {
parentFile.mkdirs()
writeText("<!-- nothing to see here -->")
}
val locationToResolveFrom = File(tempFolder.root, "a-subdirectory").apply { mkdirs() }
extension.root.set(locationToResolveFrom)
val actual = detectedCliPath(project.projectDir, extension)
val actual = detectedCliFile(extension)
assertEquals(expected.canonicalPath, actual)
assertEquals("<!-- nothing to see here -->", actual.readText())
}
@Test(expected = IllegalStateException::class)
@@ -143,7 +91,7 @@ class PathUtilsTest {
val project = ProjectBuilder.builder().build()
val extension = TestReactExtension(project)
detectedCliPath(project.projectDir, extension)
detectedCliFile(extension)
}
@Test
+2 -3
View File
@@ -76,18 +76,17 @@ plugins {
*/
react {
cliPath = "../../../../cli.js"
cliFile = file("$rootDir/cli.js")
bundleAssetName = "RNTesterApp.android.bundle"
entryFile = file("../../js/RNTesterApp.android.js")
root = file("../../")
composeSourceMapsPath = "$rootDir/scripts/compose-source-maps.js"
hermesCommand = "$rootDir/ReactAndroid/hermes-engine/build/hermes/bin/hermesc"
debuggableVariants = ["hermesDebug", "jscDebug"]
enableHermesOnlyInVariants = ["hermesDebug", "hermesRelease"]
// Codegen Configs
reactNativeDir = rootDir
codegenDir = new File(rootDir, "node_modules/react-native-codegen")
codegenDir = file("$rootDir/node_modules/react-native-codegen")
}
/**