diff --git a/.clang-format-ignore b/.clang-format-ignore index c955f0ad4d5d..8d232f8c190b 100644 --- a/.clang-format-ignore +++ b/.clang-format-ignore @@ -1,3 +1,6 @@ +**/Pods/** +**/build/** +**/node_modules/** packages/react-native/React/I18n/FBXXHashUtils.h packages/react-native/ReactAndroid/src/main/jni/first-party/yogajni/** packages/react-native/ReactAndroid/src/main/jni/third-party/** diff --git a/package.json b/package.json index cdba8ff29db9..55a1e7842346 100644 --- a/package.json +++ b/package.json @@ -9,14 +9,17 @@ "build-android": "./gradlew :packages:react-native:ReactAndroid:build", "build": "node ./scripts/build/build.js", "build-types": "node ./scripts/js-api/build-types", - "clang-format": "node ./scripts/clang-format.js", "clean": "node ./scripts/build/clean.js", "cxx-api-build": "python -m scripts.cxx-api.parser", "cxx-api-validate": "python -m scripts.cxx-api.parser --validate", "flow-check": "flow full-check", "flow": "flow", - "format-check": "prettier --list-different \"./**/*.{js,md,yml,ts,tsx}\"", - "format": "npm run prettier && npm run clang-format", + "format-check": "yarn format-check-javascript && yarn format-check-cpp", + "format-check-cpp": "node ./scripts/clang-format.js --check", + "format-check-javascript": "prettier --check \"./**/*.{cjs,cts,flow,js,jsx,md,mjs,mts,ts,tsx,yaml,yml}\"", + "format": "yarn format-javascript && yarn format-cpp", + "format-cpp": "node ./scripts/clang-format.js", + "format-javascript": "prettier --write \"./**/*.{cjs,cts,flow,js,jsx,md,mjs,mts,ts,tsx,yaml,yml}\"", "featureflags": "yarn --cwd packages/react-native featureflags", "js-api-diff": "node ./scripts/js-api/diff-api-snapshot", "lint-kotlin-check": "./gradlew ktfmtCheck", @@ -24,7 +27,6 @@ "lint-markdown": "markdownlint-cli2 2>&1", "lint": "eslint --max-warnings 0 .", "preinstall": "node ./scripts/try-set-hermes-compiler-prebuilt.js", - "prettier": "prettier --write \"./**/*.{js,md,yml,ts,tsx}\"", "shellcheck": "./.github/workflow-scripts/analyze_scripts.sh", "start": "yarn --cwd packages/rn-tester start", "set-version": "node ./scripts/releases/set-version.js", diff --git a/scripts/clang-format.js b/scripts/clang-format.js index abdf8c81df45..9b7181374b19 100644 --- a/scripts/clang-format.js +++ b/scripts/clang-format.js @@ -10,18 +10,49 @@ 'use strict'; -const dotslash = require('fb-dotslash'); const {spawnSync} = require('node:child_process'); const fs = require('node:fs'); const path = require('node:path'); const {globSync} = require('tinyglobby'); const REPO_ROOT = path.resolve(__dirname, '..'); -const CLANG_FORMAT = path.join(__dirname, 'clang-format'); +const OSS_CLANG_FORMAT_DOTSLASH = path.join(__dirname, 'clang-format'); const GENERATED_MARKER = Buffer.from('@' + 'generated'); +const IGNORE_FILE = path.join(REPO_ROOT, '.clang-format-ignore'); const MAX_HEADER_BYTES = 4096; const MAX_FILES_PER_PROCESS = 30; +const SOURCE_GLOB = '**/*.{c,cc,cpp,cu,cuh,cxx,h,hh,hpp,hxx,m,mm,proto,tcc}'; + +function findClangFormat() { + if (process.env.CLANG_FORMAT != null && process.env.CLANG_FORMAT !== '') { + return {command: process.env.CLANG_FORMAT, prefixArguments: []}; + } + + try { + const metaClangFormat = require('./clang-format.fb').findMetaClangFormat(); + if (metaClangFormat != null) { + return metaClangFormat; + } + } catch (error) { + if ( + error == null || + error.code !== 'MODULE_NOT_FOUND' || + !String(error.message).includes("'./clang-format.fb'") + ) { + throw error; + } + } + + return { + command: + process.env.DOTSLASH != null && process.env.DOTSLASH !== '' + ? process.env.DOTSLASH + : require('fb-dotslash'), + prefixArguments: [OSS_CLANG_FORMAT_DOTSLASH], + }; +} + /** @param {string} file */ function isGenerated(file) { let fd; @@ -41,16 +72,33 @@ function isGenerated(file) { } function main() { + const arguments_ = process.argv.slice(2); + const check = arguments_.includes('--check'); + const clangFormat = findClangFormat(); + const positionalArguments = arguments_.filter( + argument => argument !== '--check', + ); + const ignore = fs + .readFileSync(IGNORE_FILE, 'utf8') + .split('\n') + .map(line => line.trim()) + .filter(line => line !== '' && !line.startsWith('#')); + const discoveredFiles = globSync(SOURCE_GLOB, {cwd: REPO_ROOT, ignore}); const files = - process.argv.length > 2 - ? process.argv.slice(2) - : globSync('*/**/*.{h,cpp,m,mm}', {cwd: REPO_ROOT}); + positionalArguments.length > 0 + ? positionalArguments.filter(file => discoveredFiles.includes(file)) + : discoveredFiles; const sourceFiles = files.filter(file => !isGenerated(file)); + let exitStatus = 0; for (let i = 0; i < sourceFiles.length; i += MAX_FILES_PER_PROCESS) { + const formatterArguments = [ + ...(check ? ['--dry-run', '--Werror'] : ['-i']), + ...sourceFiles.slice(i, i + MAX_FILES_PER_PROCESS), + ]; const result = spawnSync( - dotslash, - [CLANG_FORMAT, '-i', ...sourceFiles.slice(i, i + MAX_FILES_PER_PROCESS)], + clangFormat.command, + [...clangFormat.prefixArguments, ...formatterArguments], { cwd: REPO_ROOT, stdio: 'inherit', @@ -61,13 +109,13 @@ function main() { throw result.error; } if (result.signal != null) { - process.kill(process.pid, result.signal); - return; + throw new Error(`clang-format was terminated by ${result.signal}`); } if (result.status !== 0) { - process.exit(result.status ?? 1); + exitStatus = result.status ?? 1; } } + process.exitCode = exitStatus; } main();