Skip to content

Commit 8daed7a

Browse files
[RN] Make yarn the source of truth for native (C++/Kotlin/Java) format
## Summary Extend yarn-owned formatting to native sources so GitHub CI is the single source of truth for C/C++/Obj-C, Kotlin, and Java, using the OSS toolchain so internal (Meta) and GitHub produce byte-identical output: - Add `scripts/lint/clang-format.sh` (pins clang-format `21.1.2` from PyPI), `scripts/lint/ktfmt.sh` (pins ktfmt `0.64` from Maven Central; JDK 17+), and `scripts/lint/google-java-format.sh` (pins google-java-format `1.23.0` from Maven Central; JDK 17+). Each derives its file list from `git ls-files` and honors the existing vendored/generated exclusions. - Point `yarn clang-format` + `yarn clang-format-check`, `yarn lint-kotlin` + `yarn lint-kotlin-check`, and `yarn lint-java` + `yarn lint-java-check` at those scripts. `yarn f` now runs Prettier + clang-format + ktfmt + google-java-format. - Run `clang-format-check`, `ktfmt` (Kotlin), and `google-java-format` (Java) in the CI `lint` job (JDK 17 via `setup-java`). ktfmt was previously disabled in gradle ("handled inside fbsource"); the repo now owns it. - Reformat 78 Kotlin files that differed under ktfmt `0.64` (meta style: 2-space block indent, 4-space continuation, 100 column, keep unused imports). Java is already clean under `1.23.0` (Meta uses the same release), so no Java reformat. ## Changelog [Internal] ## Test Plan `yarn clang-format-check`, `yarn lint-kotlin-check`, and `yarn lint-java-check` (JDK 17) all clean on the synced tree; `yarn f` formats JS + native. The GitHub `lint` job runs all three native checks on this PR. Changelog: [Internal]
1 parent 94717c1 commit 8daed7a

83 files changed

Lines changed: 2335 additions & 1994 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/test-all.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,20 @@ jobs:
514514
- name: TypeScript (legacy deep imports / manual types)
515515
shell: bash
516516
run: yarn test-typescript-legacy
517+
- name: clang-format (C/C++/Obj-C)
518+
shell: bash
519+
run: yarn run clang-format-check
520+
- name: Set up JDK 17
521+
uses: actions/setup-java@v5
522+
with:
523+
java-version: '17'
524+
distribution: 'zulu'
525+
- name: ktfmt (Kotlin)
526+
shell: bash
527+
run: yarn run lint-kotlin-check
528+
- name: google-java-format (Java)
529+
shell: bash
530+
run: yarn run lint-java-check
517531

518532
test_js:
519533
runs-on: ubuntu-latest

package.json

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,22 @@
99
"build-android": "./gradlew :packages:react-native:ReactAndroid:build",
1010
"build": "node ./scripts/build/build.js",
1111
"build-types": "node ./scripts/js-api/build-types",
12-
"clang-format": "clang-format -i --glob=*/**/*.{h,cpp,m,mm}",
12+
"clang-format": "./scripts/lint/clang-format.sh",
13+
"clang-format-check": "./scripts/lint/clang-format.sh --check",
1314
"clean": "node ./scripts/build/clean.js",
1415
"cxx-api-build": "python -m scripts.cxx-api.parser",
1516
"cxx-api-validate": "python -m scripts.cxx-api.parser --validate",
1617
"flow-check": "flow full-check",
1718
"flow": "flow",
18-
"f": "yarn prettier",
19+
"f": "yarn prettier && yarn clang-format && yarn lint-kotlin && yarn lint-java",
1920
"format-check": "prettier --list-different \"./**/*.{cjs,cts,flow,js,jsx,md,mjs,mts,ts,tsx,yaml,yml}\"",
20-
"format": "yarn prettier && yarn clang-format",
21+
"format": "yarn prettier && yarn clang-format && yarn lint-kotlin && yarn lint-java",
2122
"featureflags": "yarn --cwd packages/react-native featureflags",
2223
"js-api-diff": "node ./scripts/js-api/diff-api-snapshot",
23-
"lint-kotlin-check": "./gradlew ktfmtCheck",
24-
"lint-kotlin": "./gradlew ktfmtFormat",
24+
"lint-java-check": "./scripts/lint/google-java-format.sh --check",
25+
"lint-java": "./scripts/lint/google-java-format.sh",
26+
"lint-kotlin-check": "./scripts/lint/ktfmt.sh --check",
27+
"lint-kotlin": "./scripts/lint/ktfmt.sh",
2528
"lint-markdown": "markdownlint-cli2 2>&1",
2629
"lint": "eslint --max-warnings 0 .",
2730
"preinstall": "node ./scripts/try-set-hermes-compiler-prebuilt.js",

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

Lines changed: 46 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -200,42 +200,44 @@ class ReactPlugin : Plugin<Project> {
200200
}
201201

202202
// We create the tasks to produce schema from JS files and generate artifacts from schema.
203-
val generateCodegenArtifactsTask = registerCodegenTasks(
204-
project = project,
205-
rootExtension = rootExtension,
206-
generatedSrcDir = generatedSrcDir,
207-
packageJsonFile = { findPackageJsonFile(project, rootExtension.root) },
208-
schemaTaskName = "generateCodegenSchemaFromJavaScript",
209-
artifactsTaskName = "generateCodegenArtifactsFromSchema",
210-
configureJsRoot = { task, packageJson ->
211-
// We're reading the package.json at configuration time to properly feed
212-
// the `jsRootDir` @Input property of this task & the onlyIf. Therefore, the
213-
// parsePackageJson should be invoked inside this lambda.
214-
val parsedPackageJson = packageJson?.let { JsonUtils.fromPackageJson(it) }
215-
val jsSrcsDirInPackageJson = parsedPackageJson?.codegenConfig?.jsSrcsDir
216-
217-
if (packageJson != null && jsSrcsDirInPackageJson != null) {
218-
task.jsRootDir.set(File(packageJson.parentFile, jsSrcsDirInPackageJson))
219-
} else {
220-
task.jsRootDir.set(localExtension.jsRootDir)
221-
}
222-
},
223-
configureCodegenArtifacts = { task, _ ->
224-
task.codegenJavaPackageName.set(localExtension.codegenJavaPackageName)
225-
task.libraryName.set(localExtension.libraryName)
226-
},
227-
onlyIf = { packageJson ->
228-
// Please note that needsCodegenFromPackageJson is triggering a read of the
229-
// package.json at configuration time as we need to feed the onlyIf condition of this
230-
// task. Therefore, needsCodegenFromPackageJson needs to be invoked inside this
231-
// lambda.
232-
val needsCodegenFromPackageJson = project.needsCodegenFromPackageJson(rootExtension.root)
233-
val parsedPackageJson = packageJson?.let { JsonUtils.fromPackageJson(it) }
234-
val includesGeneratedCode =
235-
parsedPackageJson?.codegenConfig?.includesGeneratedCode ?: false
236-
(isLibrary || needsCodegenFromPackageJson) && !includesGeneratedCode
237-
},
238-
)
203+
val generateCodegenArtifactsTask =
204+
registerCodegenTasks(
205+
project = project,
206+
rootExtension = rootExtension,
207+
generatedSrcDir = generatedSrcDir,
208+
packageJsonFile = { findPackageJsonFile(project, rootExtension.root) },
209+
schemaTaskName = "generateCodegenSchemaFromJavaScript",
210+
artifactsTaskName = "generateCodegenArtifactsFromSchema",
211+
configureJsRoot = { task, packageJson ->
212+
// We're reading the package.json at configuration time to properly feed
213+
// the `jsRootDir` @Input property of this task & the onlyIf. Therefore, the
214+
// parsePackageJson should be invoked inside this lambda.
215+
val parsedPackageJson = packageJson?.let { JsonUtils.fromPackageJson(it) }
216+
val jsSrcsDirInPackageJson = parsedPackageJson?.codegenConfig?.jsSrcsDir
217+
218+
if (packageJson != null && jsSrcsDirInPackageJson != null) {
219+
task.jsRootDir.set(File(packageJson.parentFile, jsSrcsDirInPackageJson))
220+
} else {
221+
task.jsRootDir.set(localExtension.jsRootDir)
222+
}
223+
},
224+
configureCodegenArtifacts = { task, _ ->
225+
task.codegenJavaPackageName.set(localExtension.codegenJavaPackageName)
226+
task.libraryName.set(localExtension.libraryName)
227+
},
228+
onlyIf = { packageJson ->
229+
// Please note that needsCodegenFromPackageJson is triggering a read of the
230+
// package.json at configuration time as we need to feed the onlyIf condition of this
231+
// task. Therefore, needsCodegenFromPackageJson needs to be invoked inside this
232+
// lambda.
233+
val needsCodegenFromPackageJson =
234+
project.needsCodegenFromPackageJson(rootExtension.root)
235+
val parsedPackageJson = packageJson?.let { JsonUtils.fromPackageJson(it) }
236+
val includesGeneratedCode =
237+
parsedPackageJson?.codegenConfig?.includesGeneratedCode ?: false
238+
(isLibrary || needsCodegenFromPackageJson) && !includesGeneratedCode
239+
},
240+
)
239241

240242
// We update the android configuration to include the generated sources.
241243
// This is equivalent to this DSL:
@@ -353,13 +355,14 @@ class ReactPlugin : Plugin<Project> {
353355
project.rootProject.layout.buildDirectory.file("generated/autolinking/autolinking.json")
354356
val pureCxxDependencies =
355357
getPureCxxCodegenDependencies(rootGeneratedAutolinkingFile.get().asFile)
356-
val pureCxxCodegenTasks = configurePureCxxDependenciesCodegen(
357-
project,
358-
extension,
359-
rootExtension,
360-
generatedPureCxxSourceDir,
361-
pureCxxDependencies,
362-
)
358+
val pureCxxCodegenTasks =
359+
configurePureCxxDependenciesCodegen(
360+
project,
361+
extension,
362+
rootExtension,
363+
generatedPureCxxSourceDir,
364+
pureCxxDependencies,
365+
)
363366

364367
// We add a task called generateAutolinkingPackageList to do not clash with the existing task
365368
// called generatePackageList. This can to be renamed once we unlink the rn <-> cli

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,13 @@ abstract class BundleHermesCTask : DefaultTask() {
109109

110110
val reactNativeDir = reactNativeDir.get().asFile
111111
val composeScriptFile = File(reactNativeDir, "scripts/compose-source-maps.js")
112-
val composeSourceMapsCommand = getComposeSourceMapsCommand(
113-
composeScriptFile,
114-
packagerSourceMap,
115-
compilerSourceMap,
116-
outputSourceMap,
117-
)
112+
val composeSourceMapsCommand =
113+
getComposeSourceMapsCommand(
114+
composeScriptFile,
115+
packagerSourceMap,
116+
compilerSourceMap,
117+
outputSourceMap,
118+
)
118119
runCommand(composeSourceMapsCommand)
119120
}
120121
}

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

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,12 @@ import org.gradle.api.file.DirectoryProperty
2828
internal fun detectedEntryFile(
2929
config: ReactExtension,
3030
envVariableOverride: String? = null,
31-
): File = detectEntryFile(
32-
entryFile = config.entryFile.orNull?.asFile,
33-
reactRoot = config.root.get().asFile,
34-
envVariableOverride = envVariableOverride,
35-
)
31+
): File =
32+
detectEntryFile(
33+
entryFile = config.entryFile.orNull?.asFile,
34+
reactRoot = config.root.get().asFile,
35+
envVariableOverride = envVariableOverride,
36+
)
3637

3738
/**
3839
* Computes the CLI file for React Native. The Algo follows this order:
@@ -41,11 +42,12 @@ internal fun detectedEntryFile(
4142
* 3. The `node_modules/react-native/cli.js` file if exists
4243
* 4. Fails otherwise
4344
*/
44-
internal fun detectedCliFile(config: ReactExtension): File = detectCliFile(
45-
project = config.project,
46-
reactNativeRoot = config.root.get().asFile,
47-
preconfiguredCliFile = config.cliFile.asFile.orNull,
48-
)
45+
internal fun detectedCliFile(config: ReactExtension): File =
46+
detectCliFile(
47+
project = config.project,
48+
reactNativeRoot = config.root.get().asFile,
49+
preconfiguredCliFile = config.cliFile.asFile.orNull,
50+
)
4951

5052
/**
5153
* Computes the `hermesc` command location. The Algo follows this order:

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

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,12 @@ class ReactPluginTest {
127127

128128
@Test
129129
fun taskNameSuffixForDependency_withNonAlphanumericCharacters_encodesThem() {
130-
val dependency = ModelAutolinkingDependenciesJson(
131-
root = "./node_modules/@foo/bar-baz",
132-
name = "@foo/bar-baz",
133-
platforms = null,
134-
)
130+
val dependency =
131+
ModelAutolinkingDependenciesJson(
132+
root = "./node_modules/@foo/bar-baz",
133+
name = "@foo/bar-baz",
134+
platforms = null,
135+
)
135136

136137
val result = ReactPlugin().taskNameSuffixForDependency(dependency)
137138

@@ -143,11 +144,12 @@ class ReactPluginTest {
143144
val plugin = ReactPlugin()
144145
val suffixes =
145146
listOf("@foo/bar", "foo.bar", "foo-bar", "foo_bar", "foo_45_bar").map { name ->
146-
val dependency = ModelAutolinkingDependenciesJson(
147-
root = "./node_modules/$name",
148-
name = name,
149-
platforms = null,
150-
)
147+
val dependency =
148+
ModelAutolinkingDependenciesJson(
149+
root = "./node_modules/$name",
150+
name = name,
151+
platforms = null,
152+
)
151153

152154
plugin.taskNameSuffixForDependency(dependency)
153155
}
@@ -157,11 +159,12 @@ class ReactPluginTest {
157159

158160
@Test
159161
fun taskNameSuffixForDependency_withLocalModuleRoot_usesPackageName() {
160-
val dependency = ModelAutolinkingDependenciesJson(
161-
root = "./modules/local-module",
162-
name = "local-module",
163-
platforms = null,
164-
)
162+
val dependency =
163+
ModelAutolinkingDependenciesJson(
164+
root = "./modules/local-module",
165+
name = "local-module",
166+
platforms = null,
167+
)
165168

166169
val result = ReactPlugin().taskNameSuffixForDependency(dependency)
167170

packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/model/ModelAutolinkingDependenciesJsonTest.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ class ModelAutolinkingDependenciesJsonTest {
3636
assertThat(ModelAutolinkingDependenciesJson("", "@react-native/package", null).nameCleansed)
3737
.isEqualTo("react-native_package")
3838
assertThat(
39-
ModelAutolinkingDependenciesJson(
40-
"",
41-
"@this*is~a(more)complicated/example!of~weird)packages",
42-
null,
39+
ModelAutolinkingDependenciesJson(
40+
"",
41+
"@this*is~a(more)complicated/example!of~weird)packages",
42+
null,
43+
)
44+
.nameCleansed
4345
)
44-
.nameCleansed
45-
)
4646
.isEqualTo("this_is_a_more_complicated_example_of_weird_packages")
4747
}
4848
}

0 commit comments

Comments
 (0)