diff --git a/.github/actions/setup-react-native/action.yml b/.github/actions/setup-react-native/action.yml index 0cb2d608a..b3ae25f11 100644 --- a/.github/actions/setup-react-native/action.yml +++ b/.github/actions/setup-react-native/action.yml @@ -14,8 +14,6 @@ runs: - name: Set up react-native@${{ inputs.version }} run: | rm example/ios/Podfile.lock - rm example/macos/Podfile.lock - rm example/visionos/Podfile.lock node --run set-react-version -- ${{ inputs.version }} shell: bash working-directory: packages/app diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 22ec286d3..146189e32 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,6 +58,9 @@ jobs: yarn dedupe --check - name: Install npm dependencies uses: ./.github/actions/yarn + - name: Lint dependencies + run: | + yarn rnx-align-deps - name: Lint lockfile run: | yarn lint-lockfile @@ -358,7 +361,7 @@ jobs: uses: ./.github/actions/setup-toolchain with: platform: macos - project-root: packages/app/example + project-root: packages/example-macos cache-key-prefix: example xcode-developer-dir: /Applications/Xcode_16.4.app/Contents/Developer - name: Set up react-native@canary @@ -373,7 +376,7 @@ jobs: - name: Bundle JavaScript run: | yarn build:macos - working-directory: packages/app/example + working-directory: packages/example-macos - name: Determine whether the macOS app needs to be built id: affected uses: ./.github/actions/affected @@ -382,22 +385,22 @@ jobs: uses: ./.github/actions/cocoapods with: project-directory: macos - working-directory: packages/app/example + working-directory: packages/example-macos - name: Build if: ${{ steps.affected.outputs.macos != '' }} run: | - ../../../scripts/xcodebuild.sh macos/Example.xcworkspace build-for-testing - working-directory: packages/app/example + ../../scripts/xcodebuild.sh macos/Example.xcworkspace build-for-testing + working-directory: packages/example-macos - name: Test `react-native config` if: ${{ steps.affected.outputs.macos != '' }} run: | node --test test/config.test.mjs - working-directory: packages/app/example + working-directory: packages/example-macos - name: Test if: ${{ steps.affected.outputs.macos != '' && github.event_name != 'schedule' }} run: | - ../../../scripts/xcodebuild.sh macos/Example.xcworkspace test-without-building - working-directory: packages/app/example + ../../scripts/xcodebuild.sh macos/Example.xcworkspace test-without-building + working-directory: packages/example-macos timeout-minutes: 60 macos-template: name: "macOS [template]" @@ -571,14 +574,15 @@ jobs: - name: Bundle JavaScript run: | yarn build:windows + working-directory: packages/example-windows - name: Generate Visual Studio solution run: | - node ../windows/app.mjs --msbuildprops WindowsTargetPlatformVersion=$env:WindowsTargetPlatformVersion - working-directory: packages/app/example + node ../app/windows/app.mjs --msbuildprops WindowsTargetPlatformVersion=$env:WindowsTargetPlatformVersion + working-directory: packages/example-windows - name: Test `react-native config` run: | node --test test/config.test.mjs - working-directory: packages/app/example + working-directory: packages/example-windows - name: Determine whether the Windows app needs to be built id: affected uses: ./.github/actions/affected @@ -591,7 +595,7 @@ jobs: } else { yarn ci:windows --arch ${{ matrix.platform }} } - working-directory: packages/app/example + working-directory: packages/example-windows continue-on-error: true # GitHub Actions currently does not support outputs from jobs with matrices # The workaround is to upload artifacts to communicate between jobs @@ -617,15 +621,15 @@ jobs: uses: actions/upload-artifact@v7 with: name: windows-msbuild.binlog - path: packages/app/example/windows/*.binlog + path: packages/example-windows/windows/*.binlog retention-days: 14 overwrite: true - name: Test if: ${{ steps.affected.outputs.windows != '' && matrix.platform == 'x64' && steps.build.outcome == 'success' }} run: | - ../../../scripts/build/MSBuild.ps1 -Configuration ${{ matrix.configuration }} -Platform ${{ matrix.platform }} -Target Build ReactAppTests.vcxproj - ../../../scripts/build/VSTest.ps1 ${{ matrix.platform }}\${{ matrix.configuration }}\ReactAppTests.dll - working-directory: packages/app/example/windows/ReactAppTests + ../../../app/scripts/build/MSBuild.ps1 -Configuration ${{ matrix.configuration }} -Platform ${{ matrix.platform }} -Target Build ReactAppTests.vcxproj + ../../../app/scripts/build/VSTest.ps1 ${{ matrix.platform }}\${{ matrix.configuration }}\ReactAppTests.dll + working-directory: packages/example-windows/windows/ReactAppTests timeout-minutes: 60 windows-template: name: "Windows [template]" diff --git a/.gitignore b/.gitignore index 12a90f89a..21f016ce9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*.binlog *.iml *.log *.tgz @@ -17,18 +18,19 @@ !.yarn/patches/ !.yarn/plugins/ !.yarn/releases/ -Pods/ clang-format-diff.py coverage/ local.properties -msbuild.binlog node_modules/ !test/__fixtures__/**/node_modules/ package-lock.json -packages/app/android/**/build/ -packages/app/example/*/DerivedData/ -packages/app/example/*/build/ packages/app/example/Screenshot-*.png +packages/example-*/*/.xcode.env +packages/example-*/*/DerivedData/ +packages/example-*/*/Pods/ +packages/example-*/*/build/ +packages/example-*/dist/* +!packages/example-*/dist/.gitignore report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json template-example/ xcuserdata/ diff --git a/.yarnrc.yml b/.yarnrc.yml index d54ae8229..cbbe43ddf 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1,3 +1,10 @@ +catalog: + "@babel/core": ^7.25.2 + "@babel/preset-env": ^7.25.3 + "@rnx-kit/cli": ^1.0.0 + "@rnx-kit/metro-config": ^2.2.3 + "@rnx-kit/polyfills": ^0.2.0 + "@rnx-kit/tsconfig": ^3.0.1 compressionLevel: 0 enableGlobalCache: false enableScripts: false diff --git a/package.json b/package.json index dd12b67f5..b81bbbef9 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "workspaces": [ ".", "packages/app", - "packages/app/example" + "packages/app/example", + "packages/example-*" ], "scripts": { "format": "nx run-many --target format:c,format:js,format:swift", @@ -24,10 +25,12 @@ "lint:commit": "git log --format='%s' origin/trunk..HEAD | tail -1 | npx @rnx-kit/commitlint-lite@2.0.0", "lint:js": "oxlint $(git ls-files 'scripts/*.[cm][jt]s' 'scripts/*.[jt]s')", "release-notes": "node scripts/release-notes.mts", + "rnx-align-deps": "rnx-align-deps --exclude-packages @microsoft/root,react-native-test-app", "show-affected": "node scripts/affected.mts" }, "devDependencies": { "@nx/js": "^22.0.0", + "@rnx-kit/align-deps": "^3.4.5", "@rnx-kit/lint-lockfile": "^0.1.0", "@rnx-kit/oxlint-config": "^1.0.3", "@swc-node/register": "^1.11.1", diff --git a/packages/app/example/.gitignore b/packages/app/example/.gitignore index 12680029e..e546116a1 100644 --- a/packages/app/example/.gitignore +++ b/packages/app/example/.gitignore @@ -8,11 +8,17 @@ .gradle/ .idea/ .vs/ +.watchman-* .xcode.env -Pods/ -build/ +android/app/ +android/build/ dist/* !dist/.gitignore +ios/DerivedData/ +ios/Pods/ +ios/build/ local.properties -msbuild.binlog +macos/DerivedData/ +macos/Pods/ +macos/build/ node_modules/ diff --git a/packages/app/example/package.json b/packages/app/example/package.json index 07c9d2010..3c5d313bb 100644 --- a/packages/app/example/package.json +++ b/packages/app/example/package.json @@ -20,10 +20,10 @@ }, "dependencies": { "@react-native-webapis/web-storage": "^0.4.5", - "react": "19.1.0", - "react-native": "^0.81.0", - "react-native-macos": "^0.81.1", - "react-native-safe-area-context": "^5.5.2", + "react": "19.1.4", + "react-native": "^0.81.6", + "react-native-macos": "^0.81.0", + "react-native-safe-area-context": "^5.6.0", "react-native-windows": "^0.81.0" }, "devDependencies": { @@ -85,38 +85,17 @@ } } ], - "alignDeps//disabled": { - "//": "This configuration is only used for Viewfinder", + "alignDeps": { "requirements": [ - "react-native@0.79" + "react-native@0.81" ], "capabilities": [ "core-android", "core-ios", "core-macos", "core-windows", - "animation", "babel-preset-react-native", - "checkbox", - "clipboard", - "datetime-picker", - "filesystem", - "floating-action", - "gestures", - "html", - "masked-view", - "modal", - "navigation/native", - "navigation/stack", - "netinfo", - "popover", - "safe-area", - "screens", - "shimmer", - "sqlite", - "storage", - "svg", - "webview" + "safe-area" ] } } diff --git a/packages/app/example/test/config.test.mjs b/packages/app/example/test/config.test.mjs index 8761e3f4c..a2b6c99c9 100644 --- a/packages/app/example/test/config.test.mjs +++ b/packages/app/example/test/config.test.mjs @@ -5,17 +5,23 @@ import * as path from "node:path"; import { after, before, test } from "node:test"; import { URL, fileURLToPath } from "node:url"; -async function getLoadConfig() { +/** + * @param {string} cwd + */ +async function getLoadConfig(cwd) { try { - // @ts-expect-error `loadConfig` was not exported until 7.0. - return await import("@react-native-community/cli/build/tools/config"); + const config = import.meta.resolve( + "@react-native-community/cli/build/tools/config", + cwd + ); + return await import(config); } catch (_) { // `loadConfig` was made public in 7.0: // https://github.com/react-native-community/cli/pull/1464 - const { default: cli } = await import("@react-native-community/cli"); + const rncCli = import.meta.resolve("@react-native-community/cli", cwd); + const { default: cli } = await import(rncCli); return cli.loadConfig.length === 1 - ? // @ts-ignore The signature change of `loadConfig` was introduced in 14.0 - () => cli.loadConfig({}) // >=14.0 + ? () => cli.loadConfig({}) // >=14.0 : cli.loadConfig; // <14.0 } } @@ -29,9 +35,9 @@ function regexp(p) { } test("react-native config", async (t) => { - const loadConfig = await getLoadConfig(); - const currentDir = process.cwd(); + const loadConfig = await getLoadConfig(currentDir); + const projectRoot = path.sep + path.join("packages", "app"); const exampleRoot = path.join(projectRoot, "example"); const reactNativePath = path.join( @@ -90,6 +96,32 @@ test("react-native config", async (t) => { } ); + await t.test( + "contains macOS config", + { skip: process.platform === "win32" }, + () => { + const sourceDir = path.join(exampleRoot, "macos"); + const config = loadConfig(); + + equal(typeof config, "object"); + match(config.root, regexp(exampleRoot)); + match(config.reactNativePath, regexp(reactNativePath)); + equal( + config.dependencies["react-native-test-app"].name, + "react-native-test-app" + ); + notEqual(config.platforms.macos, undefined); + match(config.project.macos.sourceDir, regexp(sourceDir)); + + if (fs.existsSync("macos/Pods")) { + equal(config.project.macos.xcodeProject.name, "Example.xcworkspace"); + ok(config.project.macos.xcodeProject.isWorkspace); + } else { + equal(config.project.macos.xcodeProject, null); + } + } + ); + await t.test( "contains Windows config", { skip: process.platform !== "win32" }, diff --git a/packages/app/package.json b/packages/app/package.json index e0b5bdfe3..996daf9e7 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -116,9 +116,9 @@ "minimatch": "^10.0.0", "oxfmt": "^0.41.0", "oxlint": "^1.51.0", - "react": "19.1.0", - "react-native": "^0.81.0", - "react-native-macos": "^0.81.1", + "react": "19.1.4", + "react-native": "^0.81.6", + "react-native-macos": "^0.81.0", "react-native-windows": "^0.81.0", "suggestion-bot": "^4.0.0" }, diff --git a/packages/example-macos/.watchmanconfig b/packages/example-macos/.watchmanconfig new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/packages/example-macos/.watchmanconfig @@ -0,0 +1 @@ +{} diff --git a/packages/example-macos/android/build.gradle b/packages/example-macos/android/build.gradle new file mode 100644 index 000000000..3e00c032b --- /dev/null +++ b/packages/example-macos/android/build.gradle @@ -0,0 +1,23 @@ +buildscript { + apply(from: { + def searchDir = rootDir.toPath() + do { + def p = searchDir.resolve("node_modules/react-native-test-app/android/dependencies.gradle") + if (p.toFile().exists()) { + return p.toRealPath().toString() + } + } while (searchDir = searchDir.getParent()) + throw new GradleException("Could not find `react-native-test-app`"); + }()) + + repositories { + mavenCentral() + google() + } + + dependencies { + getReactNativeDependencies().each { dependency -> + classpath(dependency) + } + } +} diff --git a/packages/example-macos/android/gradle.properties b/packages/example-macos/android/gradle.properties new file mode 100644 index 000000000..a3e761121 --- /dev/null +++ b/packages/example-macos/android/gradle.properties @@ -0,0 +1,53 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the Gradle Daemon. The setting is +# particularly useful for configuring JVM memory settings for build performance. +# This does not affect the JVM settings for the Gradle client VM. +# The default is `-Xmx512m -XX:MaxMetaspaceSize=256m`. +org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will fork up to org.gradle.workers.max JVMs to execute +# projects in parallel. To learn more about parallel task execution, see the +# section on Gradle build performance: +# https://docs.gradle.org/current/userguide/performance.html#parallel_execution. +# Default is `false`. +#org.gradle.parallel=true + +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +#android.enableJetifier=true +# Jetifier randomly fails on these libraries +#android.jetifier.ignorelist=hermes-android,react-android + +# Use this property to specify which architecture you want to build. +# You can also override it from the CLI using +# ./gradlew -PreactNativeArchitectures=x86_64 +reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 + +# Use this property to enable support to the new architecture. +# This will allow you to use TurboModules and the Fabric render in +# your application. You should enable this flag either if you want +# to write custom TurboModules/Fabric components OR use libraries that +# are providing them. +# Note that this is incompatible with web debugging. +#newArchEnabled=true +#bridgelessEnabled=true + +# Uncomment the line below to build React Native from source. +#react.buildFromSource=true + +# Version of Android NDK to build against. +#ANDROID_NDK_VERSION=26.1.10909125 + +# Version of Kotlin to build against. +#KOTLIN_VERSION=1.8.22 diff --git a/packages/example-macos/android/settings.gradle b/packages/example-macos/android/settings.gradle new file mode 100644 index 000000000..64e1c4c9c --- /dev/null +++ b/packages/example-macos/android/settings.gradle @@ -0,0 +1,21 @@ +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + google() + } +} + +rootProject.name = "example" + +apply(from: { + def searchDir = rootDir.toPath() + do { + def p = searchDir.resolve("node_modules/react-native-test-app/test-app.gradle") + if (p.toFile().exists()) { + return p.toRealPath().toString() + } + } while (searchDir = searchDir.getParent()) + throw new GradleException("Could not find `react-native-test-app`"); +}()) +applyTestAppSettings(settings) diff --git a/packages/example-macos/app.json b/packages/example-macos/app.json new file mode 100644 index 000000000..b789dd5ff --- /dev/null +++ b/packages/example-macos/app.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://raw.githubusercontent.com/microsoft/react-native-test-app/trunk/schema.json", + "name": "Example", + "displayName": "Example", + "components": [ + { + "appKey": "Example", + "displayName": "App" + }, + { + "appKey": "Example", + "displayName": "App (modal)", + "presentationStyle": "modal" + } + ], + "resources": { + "android": [ + "dist/res", + "dist/main.android.jsbundle" + ], + "ios": [ + "dist/assets", + "dist/main.ios.jsbundle" + ], + "macos": [ + "dist/assets", + "dist/main.macos.jsbundle" + ], + "visionos": [ + "dist/assets", + "dist/main.visionos.jsbundle" + ] + } +} diff --git a/packages/example-macos/babel.config.js b/packages/example-macos/babel.config.js new file mode 100644 index 000000000..ffff9ce44 --- /dev/null +++ b/packages/example-macos/babel.config.js @@ -0,0 +1,4 @@ +module.exports = { + presets: [require.resolve("@react-native/babel-preset")], + plugins: [[require("@rnx-kit/polyfills")]], +}; diff --git a/packages/example-macos/dist/.gitignore b/packages/example-macos/dist/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/packages/example-macos/index.ts b/packages/example-macos/index.ts new file mode 100644 index 000000000..4ad2050a0 --- /dev/null +++ b/packages/example-macos/index.ts @@ -0,0 +1,6 @@ +// @react-native-webapis +import { AppRegistry } from "react-native"; +import { App } from "../app/example/src/App"; +import { name as appName } from "./app.json"; + +AppRegistry.registerComponent(appName, () => App); diff --git a/packages/example-macos/ios/Podfile b/packages/example-macos/ios/Podfile new file mode 100644 index 000000000..47ddffc2f --- /dev/null +++ b/packages/example-macos/ios/Podfile @@ -0,0 +1,18 @@ +ws_dir = Pathname.new(__dir__) +ws_dir = ws_dir.parent until + File.exist?("#{ws_dir}/node_modules/react-native-test-app/test_app.rb") || + ws_dir.expand_path.to_s == '/' +require "#{ws_dir}/node_modules/react-native-test-app/test_app.rb" + +workspace 'Example.xcworkspace' + +options = { + :fabric_enabled => false, + :hermes_enabled => false, +} + +use_test_app! options do |target| + target.tests do + pod 'Example-Tests', :path => '../../app/example' + end +end diff --git a/packages/example-macos/macos/Podfile b/packages/example-macos/macos/Podfile new file mode 100644 index 000000000..3613aa83f --- /dev/null +++ b/packages/example-macos/macos/Podfile @@ -0,0 +1,13 @@ +ws_dir = Pathname.new(__dir__) +ws_dir = ws_dir.parent until + File.exist?("#{ws_dir}/node_modules/react-native-test-app/macos/test_app.rb") || + ws_dir.expand_path.to_s == '/' +require "#{ws_dir}/node_modules/react-native-test-app/macos/test_app.rb" + +workspace 'Example.xcworkspace' + +use_test_app! :hermes_enabled => false do |target| + target.tests do + pod 'Example-Tests', :path => '../../app/example' + end +end diff --git a/packages/app/example/macos/Podfile.lock b/packages/example-macos/macos/Podfile.lock similarity index 99% rename from packages/app/example/macos/Podfile.lock rename to packages/example-macos/macos/Podfile.lock index e1acc146b..177751b0c 100644 --- a/packages/app/example/macos/Podfile.lock +++ b/packages/example-macos/macos/Podfile.lock @@ -2326,7 +2326,7 @@ PODS: DEPENDENCIES: - boost (from `../node_modules/react-native-macos/third-party-podspecs/boost.podspec`) - DoubleConversion (from `../node_modules/react-native-macos/third-party-podspecs/DoubleConversion.podspec`) - - Example-Tests (from `..`) + - Example-Tests (from `../../app/example`) - fast_float (from `../node_modules/react-native-macos/third-party-podspecs/fast_float.podspec`) - FBLazyVector (from `../node_modules/react-native-macos/Libraries/FBLazyVector`) - fmt (from `../node_modules/react-native-macos/third-party-podspecs/fmt.podspec`) @@ -2397,7 +2397,7 @@ DEPENDENCIES: - ReactAppDependencyProvider (from `build/generated/ios`) - ReactCodegen (from `build/generated/ios`) - ReactCommon/turbomodule/core (from `../node_modules/react-native-macos/ReactCommon`) - - "ReactNativeHost (from `../../node_modules/@rnx-kit/react-native-host`)" + - "ReactNativeHost (from `../../app/node_modules/@rnx-kit/react-native-host`)" - ReactTestApp-DevSupport (from `../node_modules/react-native-test-app`) - ReactTestApp-Resources (from `..`) - "RNWWebStorage (from `../node_modules/@react-native-webapis/web-storage`)" @@ -2414,7 +2414,7 @@ EXTERNAL SOURCES: DoubleConversion: :podspec: "../node_modules/react-native-macos/third-party-podspecs/DoubleConversion.podspec" Example-Tests: - :path: ".." + :path: "../../app/example" fast_float: :podspec: "../node_modules/react-native-macos/third-party-podspecs/fast_float.podspec" FBLazyVector: @@ -2555,7 +2555,7 @@ EXTERNAL SOURCES: ReactCommon: :path: "../node_modules/react-native-macos/ReactCommon" ReactNativeHost: - :path: "../../node_modules/@rnx-kit/react-native-host" + :path: "../../app/node_modules/@rnx-kit/react-native-host" ReactTestApp-DevSupport: :path: "../node_modules/react-native-test-app" ReactTestApp-Resources: @@ -2636,7 +2636,7 @@ SPEC CHECKSUMS: React-timing: 6fa0e6755deb2a960c61e4589c28475e654d2c35 React-utils: 88c9727bb85bc5a1a0630370a1ff9ba0b4da21ae ReactAppDependencyProvider: dddd83e92ff2e76331e033a983785f1f242e0485 - ReactCodegen: 98420ae488c701b0a6876ac22abc791d3d3b5664 + ReactCodegen: 6ad42e680f38b7c2b12fc083db2a709e8e8c4643 ReactCommon: 2024e8fc1841d1e289c5bc5b26aaf0e6ced45142 ReactNativeHost: ba9bdd449af5c793d28ff2610c1bec3015d014cc ReactTestApp-DevSupport: 356dd447fab548be5afb29613df2a18a1dfe7ec7 @@ -2645,6 +2645,6 @@ SPEC CHECKSUMS: SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 Yoga: 4b294bfee404e4118b81839ebf3fb44c586ec0e8 -PODFILE CHECKSUM: b11fdefe18a805a2039a19de11f4c79edd2682d8 +PODFILE CHECKSUM: 1bbe1c112a99af9746e5ca07f8bc40c7aa7a6eb5 COCOAPODS: 1.16.2 diff --git a/packages/example-macos/metro.config.js b/packages/example-macos/metro.config.js new file mode 100644 index 000000000..2c321b498 --- /dev/null +++ b/packages/example-macos/metro.config.js @@ -0,0 +1,11 @@ +const { makeMetroConfig } = require("@rnx-kit/metro-config"); +module.exports = makeMetroConfig({ + transformer: { + getTransformOptions: async () => ({ + transform: { + experimentalImportSupport: false, + inlineRequires: false, + }, + }), + }, +}); diff --git a/packages/example-macos/package.json b/packages/example-macos/package.json new file mode 100644 index 000000000..2ab4cb68b --- /dev/null +++ b/packages/example-macos/package.json @@ -0,0 +1,85 @@ +{ + "name": "example-macos", + "version": "0.0.1", + "private": true, + "scripts": { + "android": "rnx-cli run --platform android", + "build:android": "rnx-cli bundle --platform android", + "build:ios": "rnx-cli bundle --platform ios", + "build:macos": "rnx-cli bundle --platform macos", + "build:visionos": "rnx-cli bundle --platform ios", + "clean": "yarn workspace react-native-test-app clean", + "ios": "rnx-cli run --platform ios", + "macos": "rnx-cli run --platform macos", + "set-react-version": "yarn workspace react-native-test-app set-react-version", + "start": "rnx-cli start", + "visionos": "rnx-cli run --platform visionos" + }, + "dependencies": { + "@react-native-webapis/web-storage": "^0.4.5", + "react": "19.1.4", + "react-native": "^0.81.6", + "react-native-macos": "^0.81.0", + "react-native-safe-area-context": "^5.6.0" + }, + "devDependencies": { + "@babel/core": "catalog:", + "@babel/preset-env": "catalog:", + "@react-native-community/cli": "^20.0.0", + "@react-native-community/cli-platform-android": "^20.0.0", + "@react-native-community/cli-platform-ios": "^20.0.0", + "@react-native/babel-preset": "^0.81.0", + "@react-native/metro-config": "^0.81.0", + "@rnx-kit/cli": "catalog:", + "@rnx-kit/metro-config": "catalog:", + "@rnx-kit/polyfills": "catalog:", + "@rnx-kit/tsconfig": "catalog:", + "@types/react": "~19.1.0", + "react-native-test-app": "workspace:*" + }, + "rnx-kit": { + "kitType": "app", + "bundle": [ + { + "id": "main", + "entryFile": "index.ts", + "assetsDest": "dist", + "targets": [ + "android", + "ios", + "macos", + "visionos" + ], + "platforms": { + "android": { + "bundleOutput": "dist/main.android.jsbundle", + "sourcemapOutput": "dist/main.android.jsbundle.map", + "assetsDest": "dist/res" + }, + "ios": { + "bundleOutput": "dist/main.ios.jsbundle", + "sourcemapOutput": "dist/main.ios.jsbundle.map" + }, + "macos": { + "bundleOutput": "dist/main.macos.jsbundle", + "sourcemapOutput": "dist/main.macos.jsbundle.map" + }, + "visionos": { + "bundleOutput": "dist/main.visionos.jsbundle", + "sourcemapOutput": "dist/main.visionos.jsbundle.map" + } + } + } + ], + "alignDeps": { + "requirements": [ + "react-native@0.81" + ], + "capabilities": [ + "core-macos", + "babel-preset-react-native", + "safe-area" + ] + } + } +} diff --git a/packages/example-macos/react-native.config.js b/packages/example-macos/react-native.config.js new file mode 100644 index 000000000..a58af55f0 --- /dev/null +++ b/packages/example-macos/react-native.config.js @@ -0,0 +1 @@ +module.exports = require("../app/example/react-native.config.js"); diff --git a/packages/example-macos/test/config.test.mjs b/packages/example-macos/test/config.test.mjs new file mode 100644 index 000000000..66d454d5b --- /dev/null +++ b/packages/example-macos/test/config.test.mjs @@ -0,0 +1 @@ +import "../../app/example/test/config.test.mjs"; diff --git a/packages/example-macos/visionos/Podfile b/packages/example-macos/visionos/Podfile new file mode 100644 index 000000000..728da2368 --- /dev/null +++ b/packages/example-macos/visionos/Podfile @@ -0,0 +1,18 @@ +ws_dir = Pathname.new(__dir__) +ws_dir = ws_dir.parent until + File.exist?("#{ws_dir}/node_modules/react-native-test-app/visionos/test_app.rb") || + ws_dir.expand_path.to_s == '/' +require "#{ws_dir}/node_modules/react-native-test-app/visionos/test_app.rb" + +workspace 'Example.xcworkspace' + +options = { + :fabric_enabled => false, + :hermes_enabled => false, +} + +use_test_app! options do |target| + target.tests do + pod 'Example-Tests', :path => '../../app/example' + end +end diff --git a/packages/example-windows/.watchmanconfig b/packages/example-windows/.watchmanconfig new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/packages/example-windows/.watchmanconfig @@ -0,0 +1 @@ +{} diff --git a/packages/example-windows/android/build.gradle b/packages/example-windows/android/build.gradle new file mode 100644 index 000000000..3e00c032b --- /dev/null +++ b/packages/example-windows/android/build.gradle @@ -0,0 +1,23 @@ +buildscript { + apply(from: { + def searchDir = rootDir.toPath() + do { + def p = searchDir.resolve("node_modules/react-native-test-app/android/dependencies.gradle") + if (p.toFile().exists()) { + return p.toRealPath().toString() + } + } while (searchDir = searchDir.getParent()) + throw new GradleException("Could not find `react-native-test-app`"); + }()) + + repositories { + mavenCentral() + google() + } + + dependencies { + getReactNativeDependencies().each { dependency -> + classpath(dependency) + } + } +} diff --git a/packages/example-windows/android/gradle.properties b/packages/example-windows/android/gradle.properties new file mode 100644 index 000000000..a3e761121 --- /dev/null +++ b/packages/example-windows/android/gradle.properties @@ -0,0 +1,53 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the Gradle Daemon. The setting is +# particularly useful for configuring JVM memory settings for build performance. +# This does not affect the JVM settings for the Gradle client VM. +# The default is `-Xmx512m -XX:MaxMetaspaceSize=256m`. +org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will fork up to org.gradle.workers.max JVMs to execute +# projects in parallel. To learn more about parallel task execution, see the +# section on Gradle build performance: +# https://docs.gradle.org/current/userguide/performance.html#parallel_execution. +# Default is `false`. +#org.gradle.parallel=true + +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +#android.enableJetifier=true +# Jetifier randomly fails on these libraries +#android.jetifier.ignorelist=hermes-android,react-android + +# Use this property to specify which architecture you want to build. +# You can also override it from the CLI using +# ./gradlew -PreactNativeArchitectures=x86_64 +reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 + +# Use this property to enable support to the new architecture. +# This will allow you to use TurboModules and the Fabric render in +# your application. You should enable this flag either if you want +# to write custom TurboModules/Fabric components OR use libraries that +# are providing them. +# Note that this is incompatible with web debugging. +#newArchEnabled=true +#bridgelessEnabled=true + +# Uncomment the line below to build React Native from source. +#react.buildFromSource=true + +# Version of Android NDK to build against. +#ANDROID_NDK_VERSION=26.1.10909125 + +# Version of Kotlin to build against. +#KOTLIN_VERSION=1.8.22 diff --git a/packages/example-windows/android/settings.gradle b/packages/example-windows/android/settings.gradle new file mode 100644 index 000000000..64e1c4c9c --- /dev/null +++ b/packages/example-windows/android/settings.gradle @@ -0,0 +1,21 @@ +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + google() + } +} + +rootProject.name = "example" + +apply(from: { + def searchDir = rootDir.toPath() + do { + def p = searchDir.resolve("node_modules/react-native-test-app/test-app.gradle") + if (p.toFile().exists()) { + return p.toRealPath().toString() + } + } while (searchDir = searchDir.getParent()) + throw new GradleException("Could not find `react-native-test-app`"); +}()) +applyTestAppSettings(settings) diff --git a/packages/example-windows/app.json b/packages/example-windows/app.json new file mode 100644 index 000000000..1dc11015a --- /dev/null +++ b/packages/example-windows/app.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://raw.githubusercontent.com/microsoft/react-native-test-app/trunk/schema.json", + "name": "Example", + "displayName": "Example", + "components": [ + { + "appKey": "Example", + "displayName": "App" + }, + { + "appKey": "Example", + "displayName": "App (modal)", + "presentationStyle": "modal" + } + ], + "resources": { + "android": ["dist/res", "dist/main.android.jsbundle"], + "ios": ["dist/assets", "dist/main.ios.jsbundle"], + "windows": ["dist/assets", "dist/main.windows.bundle"] + } +} diff --git a/packages/example-windows/babel.config.js b/packages/example-windows/babel.config.js new file mode 100644 index 000000000..ffff9ce44 --- /dev/null +++ b/packages/example-windows/babel.config.js @@ -0,0 +1,4 @@ +module.exports = { + presets: [require.resolve("@react-native/babel-preset")], + plugins: [[require("@rnx-kit/polyfills")]], +}; diff --git a/packages/example-windows/dist/.gitignore b/packages/example-windows/dist/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/packages/example-windows/index.ts b/packages/example-windows/index.ts new file mode 100644 index 000000000..4ad2050a0 --- /dev/null +++ b/packages/example-windows/index.ts @@ -0,0 +1,6 @@ +// @react-native-webapis +import { AppRegistry } from "react-native"; +import { App } from "../app/example/src/App"; +import { name as appName } from "./app.json"; + +AppRegistry.registerComponent(appName, () => App); diff --git a/packages/example-windows/ios/Podfile b/packages/example-windows/ios/Podfile new file mode 100644 index 000000000..55a670f4f --- /dev/null +++ b/packages/example-windows/ios/Podfile @@ -0,0 +1,18 @@ +ws_dir = Pathname.new(__dir__) +ws_dir = ws_dir.parent until + File.exist?("#{ws_dir}/node_modules/react-native-test-app/test_app.rb") || + ws_dir.expand_path.to_s == '/' +require "#{ws_dir}/node_modules/react-native-test-app/test_app.rb" + +workspace 'Example.xcworkspace' + +options = { + :fabric_enabled => false, + :hermes_enabled => false, +} + +use_test_app! options do |target| + target.tests do + pod 'Example-Tests', :path => '..' + end +end diff --git a/packages/example-windows/metro.config.js b/packages/example-windows/metro.config.js new file mode 100644 index 000000000..2c321b498 --- /dev/null +++ b/packages/example-windows/metro.config.js @@ -0,0 +1,11 @@ +const { makeMetroConfig } = require("@rnx-kit/metro-config"); +module.exports = makeMetroConfig({ + transformer: { + getTransformOptions: async () => ({ + transform: { + experimentalImportSupport: false, + inlineRequires: false, + }, + }), + }, +}); diff --git a/packages/example-windows/package.json b/packages/example-windows/package.json new file mode 100644 index 000000000..27efe9f57 --- /dev/null +++ b/packages/example-windows/package.json @@ -0,0 +1,79 @@ +{ + "name": "example-windows", + "version": "0.0.1", + "private": true, + "scripts": { + "android": "rnx-cli run --platform android", + "build:android": "rnx-cli bundle --platform android", + "build:ios": "rnx-cli bundle --platform ios", + "build:windows": "rnx-cli bundle --platform windows", + "ci:windows": "rnx-cli run-windows --logging --no-packager --no-launch --no-deploy --msbuildprops UseBundle=false --no-telemetry", + "clean": "yarn workspace react-native-test-app clean", + "ios": "rnx-cli run --platform ios", + "set-react-version": "yarn workspace react-native-test-app set-react-version", + "start": "rnx-cli start", + "windows": "rnx-cli run-windows --no-packager" + }, + "dependencies": { + "@react-native-webapis/web-storage": "^0.4.5", + "react": "19.1.4", + "react-native": "^0.81.6", + "react-native-safe-area-context": "^5.6.0", + "react-native-windows": "^0.81.0" + }, + "devDependencies": { + "@babel/core": "catalog:", + "@babel/preset-env": "catalog:", + "@react-native-community/cli": "^20.0.0", + "@react-native-community/cli-platform-android": "^20.0.0", + "@react-native-community/cli-platform-ios": "^20.0.0", + "@react-native/babel-preset": "^0.81.0", + "@react-native/metro-config": "^0.81.0", + "@rnx-kit/cli": "catalog:", + "@rnx-kit/metro-config": "catalog:", + "@rnx-kit/polyfills": "catalog:", + "@rnx-kit/tsconfig": "catalog:", + "@types/react": "~19.1.0", + "react-native-test-app": "workspace:*" + }, + "rnx-kit": { + "kitType": "app", + "bundle": [ + { + "id": "main", + "entryFile": "index.ts", + "assetsDest": "dist", + "targets": [ + "android", + "ios", + "windows" + ], + "platforms": { + "android": { + "bundleOutput": "dist/main.android.jsbundle", + "sourcemapOutput": "dist/main.android.jsbundle.map", + "assetsDest": "dist/res" + }, + "ios": { + "bundleOutput": "dist/main.ios.jsbundle", + "sourcemapOutput": "dist/main.ios.jsbundle.map" + }, + "windows": { + "bundleOutput": "dist/main.windows.bundle", + "sourcemapOutput": "dist/main.windows.bundle.map" + } + } + } + ], + "alignDeps": { + "requirements": [ + "react-native@0.81" + ], + "capabilities": [ + "core-windows", + "babel-preset-react-native", + "safe-area" + ] + } + } +} diff --git a/packages/example-windows/react-native.config.js b/packages/example-windows/react-native.config.js new file mode 100644 index 000000000..a58af55f0 --- /dev/null +++ b/packages/example-windows/react-native.config.js @@ -0,0 +1 @@ +module.exports = require("../app/example/react-native.config.js"); diff --git a/packages/example-windows/test/config.test.mjs b/packages/example-windows/test/config.test.mjs new file mode 100644 index 000000000..66d454d5b --- /dev/null +++ b/packages/example-windows/test/config.test.mjs @@ -0,0 +1 @@ +import "../../app/example/test/config.test.mjs"; diff --git a/packages/example-windows/windows/.gitignore b/packages/example-windows/windows/.gitignore new file mode 100644 index 000000000..e6a6cc406 --- /dev/null +++ b/packages/example-windows/windows/.gitignore @@ -0,0 +1,35 @@ +.vs/ + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +ARM64/ +AppPackages/ +[Bb]in/ +[Dd]ebug/ +[Dd]ebugPublic/ +[Oo]bj/ +[Rr]elease/ +[Rr]eleases/ +bld/ +build/ +x64/ +x86/ + +# NuGet Packages Directory +packages/ + +**/Generated Files/** +*.binlog +*.err +*.hprof +*.sln +*.wrn +ExperimentalFeatures.props +NuGet.Config +dist/ +msbuild.binlog +node_modules/ diff --git a/packages/app/example/windows/ReactAppTests/ReactAppTests.vcxproj b/packages/example-windows/windows/ReactAppTests/ReactAppTests.vcxproj similarity index 100% rename from packages/app/example/windows/ReactAppTests/ReactAppTests.vcxproj rename to packages/example-windows/windows/ReactAppTests/ReactAppTests.vcxproj diff --git a/packages/app/example/windows/ReactAppTests/ReactAppTests.vcxproj.filters b/packages/example-windows/windows/ReactAppTests/ReactAppTests.vcxproj.filters similarity index 100% rename from packages/app/example/windows/ReactAppTests/ReactAppTests.vcxproj.filters rename to packages/example-windows/windows/ReactAppTests/ReactAppTests.vcxproj.filters diff --git a/packages/app/example/windows/ReactAppTests/Tests.cpp b/packages/example-windows/windows/ReactAppTests/Tests.cpp similarity index 100% rename from packages/app/example/windows/ReactAppTests/Tests.cpp rename to packages/example-windows/windows/ReactAppTests/Tests.cpp diff --git a/yarn.lock b/yarn.lock index ea005913f..31836e7a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2527,6 +2527,7 @@ __metadata: resolution: "@microsoft/root@workspace:." dependencies: "@nx/js": "npm:^22.0.0" + "@rnx-kit/align-deps": "npm:^3.4.5" "@rnx-kit/lint-lockfile": "npm:^0.1.0" "@rnx-kit/oxlint-config": "npm:^1.0.3" "@swc-node/register": "npm:^1.11.1" @@ -3859,15 +3860,15 @@ __metadata: languageName: node linkType: hard -"@rnx-kit/align-deps@npm:^3.4.2": - version: 3.4.2 - resolution: "@rnx-kit/align-deps@npm:3.4.2" +"@rnx-kit/align-deps@npm:^3.4.2, @rnx-kit/align-deps@npm:^3.4.5": + version: 3.4.5 + resolution: "@rnx-kit/align-deps@npm:3.4.5" dependencies: "@rnx-kit/types-kit-config": "npm:^1.0.0" "@rnx-kit/types-node": "npm:^1.0.0" bin: rnx-align-deps: lib/index.js - checksum: 10c0/d9d1859c622a9100ebea8c5a0417fc307bd1a78b96a3c32af3017b66ec5f955ee766de3ab65099de7b259661adf0fb3b358a161ef74e4bb456ddbfcc6f34432c + checksum: 10c0/491c55368d99761e904373c30158f88d9b82551e09ec55c0d3f5e1214ae617853b8a932e3442da78339e5e8d9ad840ee1e95b56de0454f1e203ff3099d136d62 languageName: node linkType: hard @@ -7931,6 +7932,56 @@ __metadata: languageName: node linkType: hard +"example-macos@workspace:packages/example-macos": + version: 0.0.0-use.local + resolution: "example-macos@workspace:packages/example-macos" + dependencies: + "@babel/core": "catalog:" + "@babel/preset-env": "catalog:" + "@react-native-community/cli": "npm:^20.0.0" + "@react-native-community/cli-platform-android": "npm:^20.0.0" + "@react-native-community/cli-platform-ios": "npm:^20.0.0" + "@react-native-webapis/web-storage": "npm:^0.4.5" + "@react-native/babel-preset": "npm:^0.81.0" + "@react-native/metro-config": "npm:^0.81.0" + "@rnx-kit/cli": "catalog:" + "@rnx-kit/metro-config": "catalog:" + "@rnx-kit/polyfills": "catalog:" + "@rnx-kit/tsconfig": "catalog:" + "@types/react": "npm:~19.1.0" + react: "npm:19.1.4" + react-native: "npm:^0.81.6" + react-native-macos: "npm:^0.81.0" + react-native-safe-area-context: "npm:^5.6.0" + react-native-test-app: "workspace:*" + languageName: unknown + linkType: soft + +"example-windows@workspace:packages/example-windows": + version: 0.0.0-use.local + resolution: "example-windows@workspace:packages/example-windows" + dependencies: + "@babel/core": "catalog:" + "@babel/preset-env": "catalog:" + "@react-native-community/cli": "npm:^20.0.0" + "@react-native-community/cli-platform-android": "npm:^20.0.0" + "@react-native-community/cli-platform-ios": "npm:^20.0.0" + "@react-native-webapis/web-storage": "npm:^0.4.5" + "@react-native/babel-preset": "npm:^0.81.0" + "@react-native/metro-config": "npm:^0.81.0" + "@rnx-kit/cli": "catalog:" + "@rnx-kit/metro-config": "catalog:" + "@rnx-kit/polyfills": "catalog:" + "@rnx-kit/tsconfig": "catalog:" + "@types/react": "npm:~19.1.0" + react: "npm:19.1.4" + react-native: "npm:^0.81.6" + react-native-safe-area-context: "npm:^5.6.0" + react-native-test-app: "workspace:*" + react-native-windows: "npm:^0.81.0" + languageName: unknown + linkType: soft + "example@workspace:packages/app/example": version: 0.0.0-use.local resolution: "example@workspace:packages/app/example" @@ -7952,10 +8003,10 @@ __metadata: appium: "npm:^3.1.1" appium-uiautomator2-driver: "npm:^7.0.0" appium-xcuitest-driver: "npm:^10.0.0" - react: "npm:19.1.0" - react-native: "npm:^0.81.0" - react-native-macos: "npm:^0.81.1" - react-native-safe-area-context: "npm:^5.5.2" + react: "npm:19.1.4" + react-native: "npm:^0.81.6" + react-native-macos: "npm:^0.81.0" + react-native-safe-area-context: "npm:^5.6.0" react-native-test-app: "workspace:*" react-native-windows: "npm:^0.81.0" webdriverio: "patch:webdriverio@npm%3A9.26.1#~/.yarn/patches/webdriverio-npm-9.20.0-664a6da575.patch" @@ -12378,7 +12429,7 @@ __metadata: languageName: node linkType: hard -"react-native-macos@npm:^0.81.1": +"react-native-macos@npm:^0.81.0": version: 0.81.5 resolution: "react-native-macos@npm:0.81.5" dependencies: @@ -12430,7 +12481,7 @@ __metadata: languageName: node linkType: hard -"react-native-safe-area-context@npm:^5.5.2": +"react-native-safe-area-context@npm:^5.6.0": version: 5.6.2 resolution: "react-native-safe-area-context@npm:5.6.2" peerDependencies: @@ -12471,9 +12522,9 @@ __metadata: oxfmt: "npm:^0.41.0" oxlint: "npm:^1.51.0" prompts: "npm:^2.4.0" - react: "npm:19.1.0" - react-native: "npm:^0.81.0" - react-native-macos: "npm:^0.81.1" + react: "npm:19.1.4" + react-native: "npm:^0.81.6" + react-native-macos: "npm:^0.81.0" react-native-windows: "npm:^0.81.0" semver: "npm:^7.3.5" suggestion-bot: "npm:^4.0.0" @@ -12559,7 +12610,7 @@ __metadata: languageName: node linkType: hard -"react-native@npm:^0.81.0": +"react-native@npm:^0.81.6": version: 0.81.6 resolution: "react-native@npm:0.81.6" dependencies: @@ -12616,10 +12667,10 @@ __metadata: languageName: node linkType: hard -"react@npm:19.1.0": - version: 19.1.0 - resolution: "react@npm:19.1.0" - checksum: 10c0/530fb9a62237d54137a13d2cfb67a7db6a2156faed43eecc423f4713d9b20c6f2728b026b45e28fcd72e8eadb9e9ed4b089e99f5e295d2f0ad3134251bdd3698 +"react@npm:19.1.4": + version: 19.1.4 + resolution: "react@npm:19.1.4" + checksum: 10c0/159257dbaf168facecc0a34825114e2b00624d315b7dca8468afcc790eadf5a49f90c0234be7492c7207239292e789e443ca419a83121898b82b0824454900f2 languageName: node linkType: hard