Skip to content

Commit 85a8ae8

Browse files
[RN] Make yarn the source of truth for native + Python formatting
Extend yarn-owned formatting to every synced non-JS language so GitHub CI is the single source of truth and internal (Meta) + GitHub produce byte-identical output, using the OSS toolchain: - `scripts/lint/clang-format.sh` — clang-format `21.1.2` (C/C++/Obj-C). - `scripts/lint/ktfmt.sh` — ktfmt `0.64` (Kotlin `.kt` and `.kts`). - `scripts/lint/google-java-format.sh` — google-java-format `1.23.0` (Java). - `scripts/lint/pyfmt.sh` — ufmt (`ruff-api` formatter + `usort`), configured in `pyproject.toml` (Python). - `yarn f` runs Prettier + all four; each has a `-check` variant. The CI `lint` job runs all of them (JDK 17 via setup-java for ktfmt/google-java-format; Python provisions ufmt from PyPI). - Reformats: 78 Kotlin `.kt` + 2 `.kts` to ktfmt `0.64`. C/C++/Obj-C, Java, and Python are already clean under the matching OSS releases, so no reformat there. [Internal] `yarn clang-format-check`, `yarn lint-kotlin-check`, `yarn lint-java-check` (JDK 17), and `yarn lint-python-check` all clean on the synced tree; `yarn f` formats JS + native + Python. The GitHub `lint` job runs all checks on this PR. Changelog: [Internal]
1 parent 12dd945 commit 85a8ae8

109 files changed

Lines changed: 3457 additions & 2809 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: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,29 @@ jobs:
514514
- name: TypeScript (legacy deep imports / manual types)
515515
shell: bash
516516
run: yarn test-typescript-legacy
517+
- name: Lint runner self-test (file enumeration)
518+
shell: bash
519+
run: ./scripts/lint/selftest.sh
520+
- name: clang-format (C/C++/Obj-C)
521+
shell: bash
522+
run: yarn run clang-format-check
523+
- name: Set up JDK 17
524+
uses: actions/setup-java@v5
525+
with:
526+
java-version: '17'
527+
distribution: 'zulu'
528+
- name: ktfmt (Kotlin)
529+
shell: bash
530+
run: yarn run lint-kotlin-check
531+
- name: Verify generated Kotlin is in sync with its generator
532+
shell: bash
533+
run: node packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/generate-nested-scroll-view.js --verify
534+
- name: google-java-format (Java)
535+
shell: bash
536+
run: yarn run lint-java-check
537+
- name: ufmt (Python)
538+
shell: bash
539+
run: yarn run lint-python-check
517540

518541
test_js:
519542
runs-on: ubuntu-latest

build.gradle.kts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -192,16 +192,17 @@ listOf("ktfmtCheck", "ktfmtFormat").forEach { taskName ->
192192

193193
allprojects {
194194
// Apply exclusions for specific files that should not be formatted
195-
val excludePatterns = listOf(
196-
"**/build/**",
197-
"**/hermes-engine/**",
198-
"**/internal/featureflags/**",
199-
"**/systeminfo/ReactNativeVersion.kt",
200-
)
195+
val excludePatterns =
196+
listOf(
197+
"**/build/**",
198+
"**/hermes-engine/**",
199+
"**/internal/featureflags/**",
200+
"**/systeminfo/ReactNativeVersion.kt",
201+
)
201202
listOf(
202-
com.ncorti.ktfmt.gradle.tasks.KtfmtCheckTask::class,
203-
com.ncorti.ktfmt.gradle.tasks.KtfmtFormatTask::class,
204-
)
203+
com.ncorti.ktfmt.gradle.tasks.KtfmtCheckTask::class,
204+
com.ncorti.ktfmt.gradle.tasks.KtfmtFormatTask::class,
205+
)
205206
.forEach { tasks.withType(it) { exclude(excludePatterns) } }
206207

207208
// Disable the problematic ktfmt script tasks due to symbolic link issues in subprojects

package.json

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,24 @@
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 && yarn lint-python",
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 && yarn lint-python",
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",
28+
"lint-python-check": "./scripts/lint/pyfmt.sh --check",
29+
"lint-python": "./scripts/lint/pyfmt.sh",
2530
"lint-markdown": "markdownlint-cli2 2>&1",
2631
"lint": "eslint --max-warnings 0 .",
2732
"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:

0 commit comments

Comments
 (0)