Repository: microsoft/react-native-test-app
Pull request #1000 (2022-07-18 13:12:07)

fix: trim embedded `app.json`
### Description

Trims unused parts of `app.json` and embeds it into code.

This also removes the SwiftLint phase as it's not useful for consumers, and we always run it separately on CI regardless.

### Platforms affected

- [x] Android
- [x] iOS
- [x] macOS
- [x] Windows

### Test plan

Verify that apps boot and that the app bundles no longer contain `app.json`:

```
yarn
cd example

# Android
yarn android

# iOS
pod install --project-directory=ios
yarn ios

# macOS
pod install --project-directory=macos
yarn ios

# Windows
yarn windows
```

### Screenshots

| Android | iOS | macOS | Windows |
| :-: | :-: | :-: | :-: |
| ![Screenshot_1658212780](https://user-images.githubusercontent.com/4123478/179682925-217ef016-7c5a-4960-bd05-c49d9b3a44d4.png) | ![image](https://user-images.githubusercontent.com/4123478/179682092-05d26900-000c-4432-949c-44c643bc922d.png) | ![Screenshot 2022-07-19 at 08 37 12](https://user-images.githubusercontent.com/4123478/179682507-1f6980ad-aa87-4714-a08b-46dd4db607a2.png) | ![image](https://user-images.githubusercontent.com/4123478/179681397-642d02d4-cc3d-4d7a-b3ea-6a687add8cb1.png) |

afoxman: To my credit, I understood a lot of what I looked at :)

Patch:
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 62c9bac..fe7cc5f 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -93,7 +93,9 @@ android {
         versionCode getVersionCode()
         versionName getVersionName()
 
-        buildConfigField "boolean", "ReactTestApp_useFabric", project.ext.react.enableFabric.toString()
+        def (appManifest, checksum) = validateManifest(rootDir)
+        buildConfigField "String", "ReactTestApp_appManifest", "\"${appManifest}\""
+        buildConfigField "String", "ReactTestApp_appManifestChecksum", "\"${checksum}\""
 
         def recommendedFlipperVersion = getFlipperRecommendedVersion(rootDir)
         buildConfigField "String",
@@ -103,6 +105,8 @@ android {
         def singleApp = getSingleAppMode()
         buildConfigField "String", "ReactTestApp_singleApp", singleApp ? "\"${singleApp}\"" : "null"
 
+        buildConfigField "boolean", "ReactTestApp_useFabric", project.ext.react.enableFabric.toString()
+
         resValue "string", "app_name", project.ext.react.appName
 
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock
index e41bace..d77421b 100644
--- a/example/ios/Podfile.lock
+++ b/example/ios/Podfile.lock
@@ -355,7 +355,6 @@ PODS:
     - React-jsi
   - ReactTestApp-Resources (1.0.0-dev)
   - SocketRocket (0.6.0)
-  - SwiftLint (0.47.1)
   - Yoga (1.14.0)
   - YogaKit (1.18.1):
     - Yoga (~> 1.14)
@@ -418,7 +417,6 @@ DEPENDENCIES:
   - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
   - ReactTestApp-DevSupport (from `../..`)
   - ReactTestApp-Resources (from `..`)
-  - SwiftLint
   - Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
 
 SPEC REPOS:
@@ -437,7 +435,6 @@ SPEC REPOS:
     - libevent
     - OpenSSL-Universal
     - SocketRocket
-    - SwiftLint
     - YogaKit
 
 EXTERNAL SOURCES:
@@ -556,9 +553,8 @@ SPEC CHECKSUMS:
   React-runtimeexecutor: b960b687d2dfef0d3761fbb187e01812ebab8b23
   ReactCommon: 095366164a276d91ea704ce53cb03825c487a3f2
   ReactTestApp-DevSupport: 19f2e33511690a213253175a9ca541d10456cb95
-  ReactTestApp-Resources: 74a1cf509f4e7962b16361ea4e73cba3648fff5d
+  ReactTestApp-Resources: ff5f151e465e890010b417ce65ca6c5de6aeccbb
   SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
-  SwiftLint: f80f1be7fa96d30e0aa68e58d45d4ea1ccaac519
   Yoga: 99652481fcd320aefa4a7ef90095b95acd181952
   YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
 
diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock
index 0122776..8be63c2 100644
--- a/example/macos/Podfile.lock
+++ b/example/macos/Podfile.lock
@@ -5,14 +5,14 @@ PODS:
   - Example-Tests (0.0.1-dev):
     - React
     - ReactTestApp-DevSupport
-  - FBLazyVector (0.68.3)
-  - FBReactNativeSpec (0.68.3):
+  - FBLazyVector (0.68.11)
+  - FBReactNativeSpec (0.68.11):
     - RCT-Folly (= 2021.06.28.00-v2)
-    - RCTRequired (= 0.68.3)
-    - RCTTypeSafety (= 0.68.3)
-    - React-Core (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - ReactCommon/turbomodule/core (= 0.68.3)
+    - RCTRequired (= 0.68.11)
+    - RCTTypeSafety (= 0.68.11)
+    - React-Core (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - ReactCommon/turbomodule/core (= 0.68.11)
   - fmt (6.2.1)
   - glog (0.3.5)
   - RCT-Folly (2021.06.28.00-v2):
@@ -26,272 +26,271 @@ PODS:
     - DoubleConversion
     - fmt (~> 6.2.1)
     - glog
-  - RCTRequired (0.68.3)
-  - RCTTypeSafety (0.68.3):
-    - FBLazyVector (= 0.68.3)
+  - RCTRequired (0.68.11)
+  - RCTTypeSafety (0.68.11):
+    - FBLazyVector (= 0.68.11)
     - RCT-Folly (= 2021.06.28.00-v2)
-    - RCTRequired (= 0.68.3)
-    - React-Core (= 0.68.3)
-  - React (0.68.3):
-    - React-Core (= 0.68.3)
-    - React-Core/DevSupport (= 0.68.3)
-    - React-Core/RCTWebSocket (= 0.68.3)
-    - React-RCTActionSheet (= 0.68.3)
-    - React-RCTAnimation (= 0.68.3)
-    - React-RCTBlob (= 0.68.3)
-    - React-RCTImage (= 0.68.3)
-    - React-RCTLinking (= 0.68.3)
-    - React-RCTNetwork (= 0.68.3)
-    - React-RCTSettings (= 0.68.3)
-    - React-RCTText (= 0.68.3)
-    - React-RCTVibration (= 0.68.3)
-  - React-callinvoker (0.68.3)
-  - React-Codegen (0.68.3):
-    - FBReactNativeSpec (= 0.68.3)
+    - RCTRequired (= 0.68.11)
+    - React-Core (= 0.68.11)
+  - React (0.68.11):
+    - React-Core (= 0.68.11)
+    - React-Core/DevSupport (= 0.68.11)
+    - React-Core/RCTWebSocket (= 0.68.11)
+    - React-RCTActionSheet (= 0.68.11)
+    - React-RCTAnimation (= 0.68.11)
+    - React-RCTBlob (= 0.68.11)
+    - React-RCTImage (= 0.68.11)
+    - React-RCTLinking (= 0.68.11)
+    - React-RCTNetwork (= 0.68.11)
+    - React-RCTSettings (= 0.68.11)
+    - React-RCTText (= 0.68.11)
+    - React-RCTVibration (= 0.68.11)
+  - React-callinvoker (0.68.11)
+  - React-Codegen (0.68.11):
+    - FBReactNativeSpec (= 0.68.11)
     - RCT-Folly (= 2021.06.28.00-v2)
-    - RCTRequired (= 0.68.3)
-    - RCTTypeSafety (= 0.68.3)
-    - React-Core (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-jsiexecutor (= 0.68.3)
-    - ReactCommon/turbomodule/core (= 0.68.3)
-  - React-Core (0.68.3):
+    - RCTRequired (= 0.68.11)
+    - RCTTypeSafety (= 0.68.11)
+    - React-Core (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-jsiexecutor (= 0.68.11)
+    - ReactCommon/turbomodule/core (= 0.68.11)
+  - React-Core (0.68.11):
     - glog
     - RCT-Folly (= 2021.06.28.00-v2)
-    - React-Core/Default (= 0.68.3)
-    - React-cxxreact (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-jsiexecutor (= 0.68.3)
-    - React-perflogger (= 0.68.3)
+    - React-Core/Default (= 0.68.11)
+    - React-cxxreact (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-jsiexecutor (= 0.68.11)
+    - React-perflogger (= 0.68.11)
     - Yoga
-  - React-Core/CoreModulesHeaders (0.68.3):
+  - React-Core/CoreModulesHeaders (0.68.11):
     - glog
     - RCT-Folly (= 2021.06.28.00-v2)
     - React-Core/Default
-    - React-cxxreact (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-jsiexecutor (= 0.68.3)
-    - React-perflogger (= 0.68.3)
+    - React-cxxreact (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-jsiexecutor (= 0.68.11)
+    - React-perflogger (= 0.68.11)
     - Yoga
-  - React-Core/Default (0.68.3):
+  - React-Core/Default (0.68.11):
     - glog
     - RCT-Folly (= 2021.06.28.00-v2)
-    - React-cxxreact (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-jsiexecutor (= 0.68.3)
-    - React-perflogger (= 0.68.3)
+    - React-cxxreact (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-jsiexecutor (= 0.68.11)
+    - React-perflogger (= 0.68.11)
     - Yoga
-  - React-Core/DevSupport (0.68.3):
+  - React-Core/DevSupport (0.68.11):
     - glog
     - RCT-Folly (= 2021.06.28.00-v2)
-    - React-Core/Default (= 0.68.3)
-    - React-Core/RCTWebSocket (= 0.68.3)
-    - React-cxxreact (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-jsiexecutor (= 0.68.3)
-    - React-jsinspector (= 0.68.3)
-    - React-perflogger (= 0.68.3)
+    - React-Core/Default (= 0.68.11)
+    - React-Core/RCTWebSocket (= 0.68.11)
+    - React-cxxreact (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-jsiexecutor (= 0.68.11)
+    - React-jsinspector (= 0.68.11)
+    - React-perflogger (= 0.68.11)
     - Yoga
-  - React-Core/RCTActionSheetHeaders (0.68.3):
+  - React-Core/RCTActionSheetHeaders (0.68.11):
     - glog
     - RCT-Folly (= 2021.06.28.00-v2)
     - React-Core/Default
-    - React-cxxreact (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-jsiexecutor (= 0.68.3)
-    - React-perflogger (= 0.68.3)
+    - React-cxxreact (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-jsiexecutor (= 0.68.11)
+    - React-perflogger (= 0.68.11)
     - Yoga
-  - React-Core/RCTAnimationHeaders (0.68.3):
+  - React-Core/RCTAnimationHeaders (0.68.11):
     - glog
     - RCT-Folly (= 2021.06.28.00-v2)
     - React-Core/Default
-    - React-cxxreact (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-jsiexecutor (= 0.68.3)
-    - React-perflogger (= 0.68.3)
+    - React-cxxreact (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-jsiexecutor (= 0.68.11)
+    - React-perflogger (= 0.68.11)
     - Yoga
-  - React-Core/RCTBlobHeaders (0.68.3):
+  - React-Core/RCTBlobHeaders (0.68.11):
     - glog
     - RCT-Folly (= 2021.06.28.00-v2)
     - React-Core/Default
-    - React-cxxreact (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-jsiexecutor (= 0.68.3)
-    - React-perflogger (= 0.68.3)
+    - React-cxxreact (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-jsiexecutor (= 0.68.11)
+    - React-perflogger (= 0.68.11)
     - Yoga
-  - React-Core/RCTImageHeaders (0.68.3):
+  - React-Core/RCTImageHeaders (0.68.11):
     - glog
     - RCT-Folly (= 2021.06.28.00-v2)
     - React-Core/Default
-    - React-cxxreact (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-jsiexecutor (= 0.68.3)
-    - React-perflogger (= 0.68.3)
+    - React-cxxreact (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-jsiexecutor (= 0.68.11)
+    - React-perflogger (= 0.68.11)
     - Yoga
-  - React-Core/RCTLinkingHeaders (0.68.3):
+  - React-Core/RCTLinkingHeaders (0.68.11):
     - glog
     - RCT-Folly (= 2021.06.28.00-v2)
     - React-Core/Default
-    - React-cxxreact (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-jsiexecutor (= 0.68.3)
-    - React-perflogger (= 0.68.3)
+    - React-cxxreact (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-jsiexecutor (= 0.68.11)
+    - React-perflogger (= 0.68.11)
     - Yoga
-  - React-Core/RCTNetworkHeaders (0.68.3):
+  - React-Core/RCTNetworkHeaders (0.68.11):
     - glog
     - RCT-Folly (= 2021.06.28.00-v2)
     - React-Core/Default
-    - React-cxxreact (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-jsiexecutor (= 0.68.3)
-    - React-perflogger (= 0.68.3)
+    - React-cxxreact (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-jsiexecutor (= 0.68.11)
+    - React-perflogger (= 0.68.11)
     - Yoga
-  - React-Core/RCTSettingsHeaders (0.68.3):
+  - React-Core/RCTSettingsHeaders (0.68.11):
     - glog
     - RCT-Folly (= 2021.06.28.00-v2)
     - React-Core/Default
-    - React-cxxreact (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-jsiexecutor (= 0.68.3)
-    - React-perflogger (= 0.68.3)
+    - React-cxxreact (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-jsiexecutor (= 0.68.11)
+    - React-perflogger (= 0.68.11)
     - Yoga
-  - React-Core/RCTTextHeaders (0.68.3):
+  - React-Core/RCTTextHeaders (0.68.11):
     - glog
     - RCT-Folly (= 2021.06.28.00-v2)
     - React-Core/Default
-    - React-cxxreact (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-jsiexecutor (= 0.68.3)
-    - React-perflogger (= 0.68.3)
+    - React-cxxreact (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-jsiexecutor (= 0.68.11)
+    - React-perflogger (= 0.68.11)
     - Yoga
-  - React-Core/RCTVibrationHeaders (0.68.3):
+  - React-Core/RCTVibrationHeaders (0.68.11):
     - glog
     - RCT-Folly (= 2021.06.28.00-v2)
     - React-Core/Default
-    - React-cxxreact (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-jsiexecutor (= 0.68.3)
-    - React-perflogger (= 0.68.3)
+    - React-cxxreact (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-jsiexecutor (= 0.68.11)
+    - React-perflogger (= 0.68.11)
     - Yoga
-  - React-Core/RCTWebSocket (0.68.3):
+  - React-Core/RCTWebSocket (0.68.11):
     - glog
     - RCT-Folly (= 2021.06.28.00-v2)
-    - React-Core/Default (= 0.68.3)
-    - React-cxxreact (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-jsiexecutor (= 0.68.3)
-    - React-perflogger (= 0.68.3)
+    - React-Core/Default (= 0.68.11)
+    - React-cxxreact (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-jsiexecutor (= 0.68.11)
+    - React-perflogger (= 0.68.11)
     - Yoga
-  - React-CoreModules (0.68.3):
+  - React-CoreModules (0.68.11):
     - RCT-Folly (= 2021.06.28.00-v2)
-    - RCTTypeSafety (= 0.68.3)
-    - React-Codegen (= 0.68.3)
-    - React-Core/CoreModulesHeaders (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-RCTImage (= 0.68.3)
-    - ReactCommon/turbomodule/core (= 0.68.3)
-  - React-cxxreact (0.68.3):
+    - RCTTypeSafety (= 0.68.11)
+    - React-Codegen (= 0.68.11)
+    - React-Core/CoreModulesHeaders (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-RCTImage (= 0.68.11)
+    - ReactCommon/turbomodule/core (= 0.68.11)
+  - React-cxxreact (0.68.11):
     - boost (= 1.76.0)
     - DoubleConversion
     - glog
     - RCT-Folly (= 2021.06.28.00-v2)
-    - React-callinvoker (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-jsinspector (= 0.68.3)
-    - React-logger (= 0.68.3)
-    - React-perflogger (= 0.68.3)
-    - React-runtimeexecutor (= 0.68.3)
-  - React-jsi (0.68.3):
+    - React-callinvoker (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-jsinspector (= 0.68.11)
+    - React-logger (= 0.68.11)
+    - React-perflogger (= 0.68.11)
+    - React-runtimeexecutor (= 0.68.11)
+  - React-jsi (0.68.11):
     - boost (= 1.76.0)
     - DoubleConversion
     - glog
     - RCT-Folly (= 2021.06.28.00-v2)
-    - React-jsi/Default (= 0.68.3)
-  - React-jsi/Default (0.68.3):
+    - React-jsi/Default (= 0.68.11)
+  - React-jsi/Default (0.68.11):
     - boost (= 1.76.0)
     - DoubleConversion
     - glog
     - RCT-Folly (= 2021.06.28.00-v2)
-  - React-jsiexecutor (0.68.3):
+  - React-jsiexecutor (0.68.11):
     - DoubleConversion
     - glog
     - RCT-Folly (= 2021.06.28.00-v2)
-    - React-cxxreact (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-perflogger (= 0.68.3)
-  - React-jsinspector (0.68.3)
-  - React-logger (0.68.3):
+    - React-cxxreact (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-perflogger (= 0.68.11)
+  - React-jsinspector (0.68.11)
+  - React-logger (0.68.11):
     - glog
-  - React-perflogger (0.68.3)
-  - React-RCTActionSheet (0.68.3):
-    - React-Core/RCTActionSheetHeaders (= 0.68.3)
-  - React-RCTAnimation (0.68.3):
+  - React-perflogger (0.68.11)
+  - React-RCTActionSheet (0.68.11):
+    - React-Core/RCTActionSheetHeaders (= 0.68.11)
+  - React-RCTAnimation (0.68.11):
     - RCT-Folly (= 2021.06.28.00-v2)
-    - RCTTypeSafety (= 0.68.3)
-    - React-Codegen (= 0.68.3)
-    - React-Core/RCTAnimationHeaders (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - ReactCommon/turbomodule/core (= 0.68.3)
-  - React-RCTBlob (0.68.3):
+    - RCTTypeSafety (= 0.68.11)
+    - React-Codegen (= 0.68.11)
+    - React-Core/RCTAnimationHeaders (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - ReactCommon/turbomodule/core (= 0.68.11)
+  - React-RCTBlob (0.68.11):
     - RCT-Folly (= 2021.06.28.00-v2)
-    - React-Codegen (= 0.68.3)
-    - React-Core/RCTBlobHeaders (= 0.68.3)
-    - React-Core/RCTWebSocket (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-RCTNetwork (= 0.68.3)
-    - ReactCommon/turbomodule/core (= 0.68.3)
-  - React-RCTImage (0.68.3):
+    - React-Codegen (= 0.68.11)
+    - React-Core/RCTBlobHeaders (= 0.68.11)
+    - React-Core/RCTWebSocket (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-RCTNetwork (= 0.68.11)
+    - ReactCommon/turbomodule/core (= 0.68.11)
+  - React-RCTImage (0.68.11):
     - RCT-Folly (= 2021.06.28.00-v2)
-    - RCTTypeSafety (= 0.68.3)
-    - React-Codegen (= 0.68.3)
-    - React-Core/RCTImageHeaders (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-RCTNetwork (= 0.68.3)
-    - ReactCommon/turbomodule/core (= 0.68.3)
-  - React-RCTLinking (0.68.3):
-    - React-Codegen (= 0.68.3)
-    - React-Core/RCTLinkingHeaders (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - ReactCommon/turbomodule/core (= 0.68.3)
-  - React-RCTNetwork (0.68.3):
+    - RCTTypeSafety (= 0.68.11)
+    - React-Codegen (= 0.68.11)
+    - React-Core/RCTImageHeaders (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-RCTNetwork (= 0.68.11)
+    - ReactCommon/turbomodule/core (= 0.68.11)
+  - React-RCTLinking (0.68.11):
+    - React-Codegen (= 0.68.11)
+    - React-Core/RCTLinkingHeaders (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - ReactCommon/turbomodule/core (= 0.68.11)
+  - React-RCTNetwork (0.68.11):
     - RCT-Folly (= 2021.06.28.00-v2)
-    - RCTTypeSafety (= 0.68.3)
-    - React-Codegen (= 0.68.3)
-    - React-Core/RCTNetworkHeaders (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - ReactCommon/turbomodule/core (= 0.68.3)
-  - React-RCTSettings (0.68.3):
+    - RCTTypeSafety (= 0.68.11)
+    - React-Codegen (= 0.68.11)
+    - React-Core/RCTNetworkHeaders (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - ReactCommon/turbomodule/core (= 0.68.11)
+  - React-RCTSettings (0.68.11):
     - RCT-Folly (= 2021.06.28.00-v2)
-    - RCTTypeSafety (= 0.68.3)
-    - React-Codegen (= 0.68.3)
-    - React-Core/RCTSettingsHeaders (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - ReactCommon/turbomodule/core (= 0.68.3)
-  - React-RCTText (0.68.3):
-    - React-Core/RCTTextHeaders (= 0.68.3)
-  - React-RCTVibration (0.68.3):
+    - RCTTypeSafety (= 0.68.11)
+    - React-Codegen (= 0.68.11)
+    - React-Core/RCTSettingsHeaders (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - ReactCommon/turbomodule/core (= 0.68.11)
+  - React-RCTText (0.68.11):
+    - React-Core/RCTTextHeaders (= 0.68.11)
+  - React-RCTVibration (0.68.11):
     - RCT-Folly (= 2021.06.28.00-v2)
-    - React-Codegen (= 0.68.3)
-    - React-Core/RCTVibrationHeaders (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - ReactCommon/turbomodule/core (= 0.68.3)
-  - React-runtimeexecutor (0.68.3):
-    - React-jsi (= 0.68.3)
-  - ReactCommon/turbomodule/core (0.68.3):
+    - React-Codegen (= 0.68.11)
+    - React-Core/RCTVibrationHeaders (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - ReactCommon/turbomodule/core (= 0.68.11)
+  - React-runtimeexecutor (0.68.11):
+    - React-jsi (= 0.68.11)
+  - ReactCommon/turbomodule/core (0.68.11):
     - DoubleConversion
     - glog
     - RCT-Folly (= 2021.06.28.00-v2)
-    - React-callinvoker (= 0.68.3)
-    - React-Core (= 0.68.3)
-    - React-cxxreact (= 0.68.3)
-    - React-jsi (= 0.68.3)
-    - React-logger (= 0.68.3)
-    - React-perflogger (= 0.68.3)
+    - React-callinvoker (= 0.68.11)
+    - React-Core (= 0.68.11)
+    - React-cxxreact (= 0.68.11)
+    - React-jsi (= 0.68.11)
+    - React-logger (= 0.68.11)
+    - React-perflogger (= 0.68.11)
   - ReactTestApp-DevSupport (0.0.1-dev):
     - React-Core
     - React-jsi
   - ReactTestApp-Resources (1.0.0-dev)
-  - SwiftLint (0.47.1)
   - Yoga (1.14.0)
 
 DEPENDENCIES:
@@ -331,13 +330,11 @@ DEPENDENCIES:
   - ReactCommon/turbomodule/core (from `../node_modules/react-native-macos/ReactCommon`)
   - ReactTestApp-DevSupport (from `../..`)
   - ReactTestApp-Resources (from `..`)
-  - SwiftLint
   - Yoga (from `../node_modules/react-native-macos/ReactCommon/yoga`)
 
 SPEC REPOS:
   trunk:
     - fmt
-    - SwiftLint
 
 EXTERNAL SOURCES:
   boost:
@@ -416,39 +413,38 @@ SPEC CHECKSUMS:
   boost-for-react-native: 8f7c9ecfe357664c072ffbe2432569667cbf1f1b
   DoubleConversion: ed15e075aa758ac0e4c1f8b830bd4e4d40d669e8
   Example-Tests: e3a0c1aa41706608d102daa2239aa6d79fcb6e51
-  FBLazyVector: 3f8ce4c4a9881e34b7007ce4f5947b3975c5c75d
-  FBReactNativeSpec: 9d7f7433a737ca6fc05ed3a25a27ff8455a26031
+  FBLazyVector: b24790c6ef94f11c4a5cec1961ba4e7afa6783a2
+  FBReactNativeSpec: 71a7210f46ea5eca8f3b49ae545f044da57998a1
   fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
   glog: 20113a0d46931b6f096cf8302c68691d75a456ff
   RCT-Folly: 5544a3ff21f4406e70e92a8711598e97fc81517c
-  RCTRequired: 083b4757f7225704924872df942560c50753aa61
-  RCTTypeSafety: 21987be471785ba53201cbd0f82f91091d2a6fa6
-  React: d898a55388e6d98211084f4ef0cade4f14b04903
-  React-callinvoker: 0cef8d64a04c7f891be7bfca703bf9ab3e6cf8c3
-  React-Codegen: b37c5593eb665af5b75731bf7b8412765e615dc0
-  React-Core: edc54e5f44a247412ea820a809e59974d83025a7
-  React-CoreModules: ccd1be9e6c318ba9ca63ec841bca831499557886
-  React-cxxreact: 8eb6996e49e7076f61698a3de6977ee6cde09636
-  React-jsi: 6466c98edc1a1f6a5ca685e25a4087dfcdac4dac
-  React-jsiexecutor: 915c73ac453e553554e0c6a46270ade50bcfef08
-  React-jsinspector: 023b255eaa15aa8261327ba0965ab02dc5669434
-  React-logger: d38c57f596319e7d4d22aec8128bc8468fd1b843
-  React-perflogger: 9f1930af8a3caae68f88b2600b41cb83a6359f8c
-  React-RCTActionSheet: 98407c473a2e1b348aad00bb157e061dd396806c
-  React-RCTAnimation: 9315b387c642bae258c69990ab0f8d063f2f5d21
-  React-RCTBlob: 50be9a438eefeaebb33cbbdb6b018700a1a8ddc0
-  React-RCTImage: 7ed32c983d5ab8a5053f0a8ceec0c554316431c3
-  React-RCTLinking: 00aebaf37d46d32347af4775d797ee79afae1c31
-  React-RCTNetwork: ab380fd6938aafccb45db7228ef8db291ed81d17
-  React-RCTSettings: 1a80ee17ec77e62f0b1c4ee97c92e71433a81199
-  React-RCTText: 5b322dcb8359a3b8e71f855b922be9e594d2ffae
-  React-RCTVibration: 223b77bf355e4966865752252d18db6c7d615a65
-  React-runtimeexecutor: 659330ef4f8c50c5a1a72c3f898bbac02fb32e65
-  ReactCommon: 0f5984ee71d16c00f7f9d59187186ba44b559f8c
+  RCTRequired: 4832e8451d7ecb4d23deb2343d73ada0d0adcc3c
+  RCTTypeSafety: c5a6c8540b071e3e5101e2a2c921a36e1013befa
+  React: 86d585bb9bb23dd4d287636493f42581d1a4d6e2
+  React-callinvoker: c394f574d942ac6a47b61f1aa955e52747f548e3
+  React-Codegen: 631bee7dfb793cf1254eebd80f709e4f7ed6802b
+  React-Core: 62e69c60621197a345ddf0ca8da43fb6bec35452
+  React-CoreModules: a2b70019c9f41318a57e17fb81a53f55d06a1ad7
+  React-cxxreact: aa6e6952a26d38c7a58adf5bf5cf2fc0b7fcd27f
+  React-jsi: 4864e4394a88c9d3369700e999ee307b89f7e2f2
+  React-jsiexecutor: f7c8d1e34341739055adfbf24eab9a710e128f3b
+  React-jsinspector: 5cd2f0769caf54e92b8a8d2df6f664822b25609a
+  React-logger: 4872174ad87732eb5edefbdfed01e9266661c0e0
+  React-perflogger: 1c00528daacaf4e526a9a4da310c20bf91e63e45
+  React-RCTActionSheet: 6eb23254d356a261576f58304f8a9d73fb1d47ab
+  React-RCTAnimation: 3c05dd4ca63c0bc7970a725e9c40ff0fccf210a9
+  React-RCTBlob: 44030599d90259ca5a0c72dd1d6ec49ca7bb8d36
+  React-RCTImage: 4a2b44615eedb2c24f18f5e5bb5969741d4c0e19
+  React-RCTLinking: 116098af22b4d5e0ac43d3cdf5f14023abe79fb5
+  React-RCTNetwork: 97e0f9f9d547fcbac93feb2493360ca8ecc960f4
+  React-RCTSettings: eca0faa9be97a99454c901b4c43bd4661a18e5ec
+  React-RCTText: c687858db16d2f0fd5be3f1d474e8a0623510582
+  React-RCTVibration: f6f5c14863b6b2291eda14dcc0a92d821d6be14a
+  React-runtimeexecutor: cc1524ec170aeea26bfbb4f2d12eb3b6d4483e56
+  ReactCommon: 68d433c583946184832af23ad1365f56010d0da4
   ReactTestApp-DevSupport: 19f2e33511690a213253175a9ca541d10456cb95
-  ReactTestApp-Resources: bb546b3a5dca4b7931bee423d4ef28cd94b346cf
-  SwiftLint: f80f1be7fa96d30e0aa68e58d45d4ea1ccaac519
-  Yoga: ef2fe89f3a5b24392ecedf6ee50bfe7ac7bd6d1f
+  ReactTestApp-Resources: 8539dac0f8d2ef3821827a537e37812104c6ff78
+  Yoga: 29710be58872297bc5505ddc7e343798359ab948
 
 PODFILE CHECKSUM: 39314e677d5ddf7e1e4c81e5e81f66cddabd661a
 
diff --git a/example/package.json b/example/package.json
index cefc2ef..42c00df 100644
--- a/example/package.json
+++ b/example/package.json
@@ -8,7 +8,7 @@
     "build:ios": "mkdirp dist && react-native bundle --entry-file index.js --platform ios --dev true --bundle-output dist/main.ios.jsbundle --assets-dest dist",
     "build:macos": "mkdirp dist && react-native bundle --entry-file index.js --platform macos --dev true --bundle-output dist/main.macos.jsbundle --assets-dest dist",
     "build:windows": "mkdirp dist && react-native bundle --entry-file index.js --platform windows --dev true --bundle-output dist/main.windows.bundle --assets-dest dist",
-    "ci:windows": "react-native run-windows --logging --no-packager --no-launch --no-deploy",
+    "ci:windows": "react-native run-windows --logging --no-packager --no-launch --no-deploy --no-telemetry",
     "clean": "yarn workspace react-native-test-app clean",
     "ios": "react-native run-ios",
     "macos": "react-native run-macos --scheme Example",
diff --git a/scripts/generate-manifest.mjs b/scripts/generate-manifest.mjs
index 0abc2f2..1e8c873 100755
--- a/scripts/generate-manifest.mjs
+++ b/scripts/generate-manifest.mjs
@@ -67,7 +67,7 @@ function getLanguage(output) {
             "{",
           ].join("\n"),
           footer: [
-            "    std::optional<std::tuple<Manifest, std::string>> GetManifest(std::string const &filename);",
+            "    std::optional<std::tuple<Manifest, std::string>> GetManifest(const char *const json = nullptr);",
             "",
             "}  // namespace ReactTestApp",
             "",
diff --git a/scripts/validate-manifest.js b/scripts/validate-manifest.js
index 5324664..c0215ea 100755
--- a/scripts/validate-manifest.js
+++ b/scripts/validate-manifest.js
@@ -6,24 +6,35 @@ const fs = require("fs");
 const path = require("path");
 
 const APP_JSON = "app.json";
+const NODE_MODULES = "node_modules";
+
+const BUILD_PROPS = [
+  "$schema",
+  "android",
+  "ios",
+  "macos",
+  "windows",
+  "resources",
+];
 
 /**
- * Finds the app manifest.
- * @param {string=} cwd
+ * Finds the specified file using Node module resolution.
+ * @param {string} file
+ * @param {string=} startDir
  * @returns {string | undefined}
  */
-function findAppManifest(cwd = process.cwd()) {
-  const candidate = path.join(cwd, APP_JSON);
-  if (fs.existsSync(candidate)) {
-    return candidate;
-  }
+function findFile(file, startDir = process.cwd()) {
+  let candidate = path.join(startDir, file);
+  while (!fs.existsSync(candidate)) {
+    const cwd = path.dirname(candidate);
+    const parent = path.dirname(cwd);
+    if (parent === cwd) {
+      return undefined;
+    }
 
-  const parent = path.dirname(cwd);
-  if (parent === cwd) {
-    return undefined;
+    candidate = path.join(parent, file);
   }
-
-  return findAppManifest(parent);
+  return candidate;
 }
 
 function makeValidator() {
@@ -43,16 +54,20 @@ function makeValidator() {
   return ajv.compile(require(`${__dirname}/../schema.json`));
 }
 
-function validateManifest(manifestPath = findAppManifest()) {
+/**
+ * @param {fs.PathLike | undefined} manifestPath
+ * @returns {Record<string, unknown> | number}
+ */
+function validateManifest(manifestPath) {
   if (!manifestPath) {
     console.error(
       `Failed to find '${APP_JSON}'. Please make sure you're in the right directory.`
     );
-    process.exit(1);
+    return 1;
   }
 
   const content = fs.readFileSync(manifestPath, { encoding: "utf-8" });
-  const manifest = JSON.parse(content);
+  const manifest = /** @type {Record<string, unknown>} */ (JSON.parse(content));
   const validate = makeValidator();
   if (!validate(manifest)) {
     console.error(
@@ -66,14 +81,82 @@ function validateManifest(manifestPath = findAppManifest()) {
         )
       );
     }
-    process.exit(1);
+    return 1000 + (errors?.length ?? 0);
+  }
+
+  for (const key of BUILD_PROPS) {
+    delete manifest[key];
+  }
+
+  return manifest;
+}
+
+/**
+ * @param {("file" | "stdout")=} outputMode Whether to output to `file` or `stdout`
+ */
+function validate(outputMode = "stdout") {
+  const manifestPath = findFile(APP_JSON);
+  const manifest = validateManifest(manifestPath);
+  if (typeof manifest === "number") {
+    process.exitCode = manifest;
+    return;
+  }
+
+  const nodeModulesPath = findFile(NODE_MODULES);
+  if (!nodeModulesPath) {
+    console.error(
+      `Failed to find '${NODE_MODULES}'. Please make sure you've installed npm dependencies.`
+    );
+    process.exitCode = 2;
+    return;
+  }
+
+  const copy = JSON.stringify(manifest);
+  const escapedCopy = copy.replace(/["]/g, '\\"');
+
+  if (outputMode === "file") {
+    const checksum = require("crypto")
+      .createHash("sha256")
+      .update(copy)
+      .digest("hex");
+    const cppHeader = [
+      "// clang-format off",
+      "#ifndef REACTTESTAPP_APP_JSON_H_",
+      "#define REACTTESTAPP_APP_JSON_H_",
+      "",
+      `const char* const ReactTestApp_AppManifest = "${escapedCopy}";`,
+      `const char* const ReactTestApp_AppManifestChecksum = "${checksum}";`,
+      `const size_t ReactTestApp_AppManifestLength = ${copy.length};`,
+      "",
+      "#endif  // REACTTESTAPP_APP_JSON_H_",
+      "",
+    ].join("\n");
+    const manifestCopyDest = path.join(
+      nodeModulesPath,
+      ".generated",
+      APP_JSON + ".h"
+    );
+
+    if (
+      !fs.existsSync(manifestCopyDest) ||
+      cppHeader !== fs.readFileSync(manifestCopyDest, { encoding: "utf-8" })
+    ) {
+      fs.mkdirSync(path.dirname(manifestCopyDest), {
+        recursive: true,
+        mode: 0o755,
+      });
+      fs.writeFileSync(manifestCopyDest, cppHeader);
+    }
+  } else {
+    console.log(escapedCopy);
   }
 }
 
-exports.findAppManifest = findAppManifest;
+exports.findFile = findFile;
+exports.validate = validate;
 exports.validateManifest = validateManifest;
 
 if (require.main === module) {
   // istanbul ignore next
-  validateManifest();
+  validate();
 }
diff --git a/yarn.lock b/yarn.lock
index efb3a50..4289150 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10649,8 +10649,8 @@ fsevents@^2.3.2:
   linkType: hard
 
 "react-native-macos@npm:^0.68.3":
-  version: 0.68.6
-  resolution: "react-native-macos@npm:0.68.6"
+  version: 0.68.11
+  resolution: "react-native-macos@npm:0.68.11"
   dependencies:
     "@jest/create-cache-key-function": ^27.0.1
     "@react-native-community/cli": ^7.0.3
@@ -10688,7 +10688,7 @@ fsevents@^2.3.2:
     react: 17.0.2
   bin:
     react-native-macos: cli.js
-  checksum: c6d34871a6612b88b9821e9d1d0bc5674e35714e1daa7f11f80ef57066f82439d4028686b060e578d98f24265f75ca6acb7368f56406ec0f7a2eff2f1b395b99
+  checksum: 7f756d25a29b9acee8706dca67a6294d26c466056f4ce9185081e0198f4f223fc11bdd20348f94a301d39bc1a0527e949f65db41de090763a4ff78eabe3c8fe5
   languageName: node
   linkType: hard
 


Test patch:
diff --git a/android/app/src/main/java/com/microsoft/reacttestapp/manifest/ManifestProvider.kt b/android/app/src/main/java/com/microsoft/reacttestapp/manifest/ManifestProvider.kt
index dcb1ef2..7f0eb4e 100644
--- a/android/app/src/main/java/com/microsoft/reacttestapp/manifest/ManifestProvider.kt
+++ b/android/app/src/main/java/com/microsoft/reacttestapp/manifest/ManifestProvider.kt
@@ -1,9 +1,9 @@
 package com.microsoft.reacttestapp.manifest
 
 import android.content.Context
+import com.microsoft.reacttestapp.BuildConfig
 import com.squareup.moshi.JsonAdapter
 import com.squareup.moshi.Moshi
-import java.security.MessageDigest
 
 class ManifestProvider(private val context: Context, private val adapter: JsonAdapter<Manifest>) {
 
@@ -18,32 +18,13 @@ class ManifestProvider(private val context: Context, private val adapter: JsonAd
     }
 
     private val manifestAndChecksum: Pair<Manifest, String> by lazy {
-        val appIdentifier = context.resources
-            .getIdentifier("raw/app", null, context.packageName)
-
-        if (appIdentifier == 0) {
-            throw IllegalStateException("Could not find `app.json` in the app bundle")
-        }
-
-        val json = context.resources
-            .openRawResource(appIdentifier)
-            .bufferedReader()
-            .use { it.readText() }
-        val manifest = adapter.fromJson(json)
+        val manifest = adapter.fromJson(BuildConfig.ReactTestApp_appManifest)
             ?: throw IllegalStateException("Could not parse `app.json`")
 
-        Pair(manifest, json.checksum("SHA-256"))
+        Pair(manifest, BuildConfig.ReactTestApp_appManifestChecksum)
     }
 
     fun fromResources(): Pair<Manifest, String> {
         return manifestAndChecksum
     }
 }
-
-fun ByteArray.toHex(): String {
-    return joinToString("") { "%02x".format(it) }
-}
-
-fun String.checksum(algorithm: String): String {
-    return MessageDigest.getInstance(algorithm).digest(toByteArray()).toHex()
-}
diff --git a/android/test-app-util.gradle b/android/test-app-util.gradle
index 9fa5ffb..6fc60b7 100644
--- a/android/test-app-util.gradle
+++ b/android/test-app-util.gradle
@@ -1,6 +1,8 @@
 import groovy.json.JsonSlurper
 
+import java.nio.charset.StandardCharsets
 import java.nio.file.Paths
+import java.security.MessageDigest
 
 ext.manifest = null
 
@@ -13,7 +15,7 @@ ext.findFile = { fileName ->
     def currentDirPath = rootDir == null ? null : rootDir.toString()
 
     while (currentDirPath != null) {
-        def currentDir = file(currentDirPath);
+        def currentDir = file(currentDirPath)
         def requestedFile = Paths.get(currentDirPath, fileName).toFile()
 
         if (requestedFile.exists()) {
@@ -220,3 +222,22 @@ ext.isFabricEnabled = { baseDir ->
         project.getProperty("USE_FABRIC") == "1" &&
         getReactNativeVersionNumber(baseDir) >= 6800
 }
+
+ext.validateManifest = { baseDir ->
+    String[] cmd = ["node", "-e", "require('react-native-test-app/scripts/validate-manifest').validate('stdout')"]
+    def process = Runtime.runtime.exec(cmd, null, baseDir)
+    process.waitFor()
+
+    def inputStream = process.getInputStream()
+    def output = new ByteArrayOutputStream()
+    def buffer = new byte[1024]
+    def length = inputStream.read(buffer)
+    while (length > 0) {
+        output.write(buffer, 0, length)
+        length = inputStream.read(buffer)
+    }
+
+    def result = output.toString(StandardCharsets.UTF_8.name()).trim()
+    def checksum = MessageDigest.getInstance("SHA-256").digest(output.toByteArray())
+    return [result, checksum.encodeHex().toString()]
+}
diff --git a/example/ios/ExampleTests/ManifestTests.swift b/example/ios/ExampleTests/ManifestTests.swift
index 3c20e83..1f914d9 100644
--- a/example/ios/ExampleTests/ManifestTests.swift
+++ b/example/ios/ExampleTests/ManifestTests.swift
@@ -2,8 +2,16 @@
 
 import XCTest
 
-class ManifestTests: XCTestCase {
+// swiftlint:disable identifier_name line_length
+let ReactTestAppTests_AppManifest: NSString = "{\"name\":\"Example\",\"displayName\":\"Example\",\"components\":[{\"appKey\":\"Example\",\"displayName\":\"App\"},{\"appKey\":\"Example\",\"displayName\":\"App (modal)\",\"presentationStyle\":\"modal\"}]}"
+let ReactTestAppTests_AppManifestChecksum: NSString = "94a9447a98dd58c301b7b178a2ee9f29aac817b4fa3688678e4f39b250d5eb37"
+
+let ReactTestApp_AppManifest: UnsafePointer<Int8>? = ReactTestAppTests_AppManifest.utf8String
+let ReactTestApp_AppManifestChecksum: UnsafePointer<Int8>! = ReactTestAppTests_AppManifestChecksum.utf8String
+let ReactTestApp_AppManifestLength = 175
+// swiftlint:enable identifier_name line_length
 
+class ManifestTests: XCTestCase {
     func testReadFromFile() {
         guard let (manifest, checksum) = Manifest.fromFile() else {
             XCTFail("Failed to read 'app.json'")
diff --git a/example/windows/ReactTestAppTests/ManifestTests.cpp b/example/windows/ReactTestAppTests/ManifestTests.cpp
index c9cc2bc..5be114c 100644
--- a/example/windows/ReactTestAppTests/ManifestTests.cpp
+++ b/example/windows/ReactTestAppTests/ManifestTests.cpp
@@ -1,6 +1,7 @@
 #include "pch.h"
 
 #include <CppUnitTest.h>
+#include <cstdio>
 #include <filesystem>
 #include <string>
 
@@ -11,7 +12,7 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework;
 using ReactTestApp::Component;
 using ReactTestApp::Manifest;
 
-std::string fixturePath(std::filesystem::path file)
+std::string readManifest(std::filesystem::path file)
 {
     // Current working directory when running tests (with VSTest.Console.exe)
     // changed between Visual Studio 16.8 and 16.9 and broke our pipelines. To
@@ -21,7 +22,19 @@ std::string fixturePath(std::filesystem::path file)
     // To ensure that `__FILE__` is a full path, we must also enable `/FC` in
     // Properties > C/C++ > Advanced.
     const auto p = std::filesystem::path(__FILE__).replace_filename("manifestTestFiles") / file;
-    return p.string();
+
+    std::FILE *stream = nullptr;
+    fopen_s(&stream, p.u8string().c_str(), "rb");
+
+    std::string json;
+    std::fseek(stream, 0, SEEK_END);
+    json.resize(std::ftell(stream));
+
+    std::rewind(stream);
+    std::fread(json.data(), 1, json.size(), stream);
+    std::fclose(stream);
+
+    return json;
 }
 
 // disable clang-format because it doesn't handle macros very well
@@ -33,7 +46,8 @@ namespace ReactTestAppTests
     public:
         TEST_METHOD(ParseManifestWithOneComponent)
         {
-            auto result = ReactTestApp::GetManifest(fixturePath("simpleManifest.json"));
+            auto json = readManifest("simpleManifest.json");
+            auto result = ReactTestApp::GetManifest(json.c_str());
             if (!result.has_value()) {
                 Assert::Fail(L"Couldn't read manifest file");
             }
@@ -52,7 +66,8 @@ namespace ReactTestAppTests
 
         TEST_METHOD(ParseManifestWithMultipleComponents)
         {
-            auto result = ReactTestApp::GetManifest(fixturePath("withMultipleComponents.json"));
+            auto json = readManifest("withMultipleComponents.json");
+            auto result = ReactTestApp::GetManifest(json.c_str());
             if (!result.has_value()) {
                 Assert::Fail(L"Couldn't read manifest file");
             }
@@ -80,7 +95,8 @@ namespace ReactTestAppTests
 
         TEST_METHOD(ParseManifestWithComplexInitialProperties)
         {
-            auto result = ReactTestApp::GetManifest(fixturePath("withComplexInitialProperties.json"));
+            auto json = readManifest("withComplexInitialProperties.json");
+            auto result = ReactTestApp::GetManifest(json.c_str());
             if (!result.has_value()) {
                 Assert::Fail(L"Couldn't read manifest file");
             }
diff --git a/example/windows/ReactTestAppTests/ReactTestAppTests.vcxproj b/example/windows/ReactTestAppTests/ReactTestAppTests.vcxproj
index 86ead1c..dd55ff5 100644
--- a/example/windows/ReactTestAppTests/ReactTestAppTests.vcxproj
+++ b/example/windows/ReactTestAppTests/ReactTestAppTests.vcxproj
@@ -1,5 +1,16 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup Label="Globals">
+    <VCProjectVersion>16.0</VCProjectVersion>
+    <ProjectGuid>{D2B221C0-0781-4D20-8BF1-D88684662A5D}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>ReactTestAppTests</RootNamespace>
+    <ProjectSubType>NativeUnitTestProject</ProjectSubType>
+  </PropertyGroup>
+  <PropertyGroup Label="NuGet">
+    <ResolveNuGetPackages>false</ResolveNuGetPackages>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|ARM">
       <Configuration>Debug</Configuration>
@@ -34,14 +45,6 @@
       <Platform>Win32</Platform>
     </ProjectConfiguration>
   </ItemGroup>
-  <PropertyGroup Label="Globals">
-    <VCProjectVersion>16.0</VCProjectVersion>
-    <ProjectGuid>{D2B221C0-0781-4D20-8BF1-D88684662A5D}</ProjectGuid>
-    <Keyword>Win32Proj</Keyword>
-    <RootNamespace>ReactTestAppTests</RootNamespace>
-    <ProjectSubType>NativeUnitTestProject</ProjectSubType>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
   <PropertyGroup Label="Configuration">
     <ConfigurationType>DynamicLibrary</ConfigurationType>
     <PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
diff --git a/ios/ReactTestApp.xcodeproj/project.pbxproj b/ios/ReactTestApp.xcodeproj/project.pbxproj
index 5bc7425..97dbbf2 100644
--- a/ios/ReactTestApp.xcodeproj/project.pbxproj
+++ b/ios/ReactTestApp.xcodeproj/project.pbxproj
@@ -175,8 +175,7 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 19ECD0FC232ED428003D8557 /* Build configuration list for PBXNativeTarget "ReactTestApp" */;
 			buildPhases = (
-				1936CB1C2768EC280085FD98 /* Validate manifest */,
-				196C722C2330D28A006556ED /* Run SwiftLint */,
+				1936CB1C2768EC280085FD98 /* Validate Manifest */,
 				19ECD0CE232ED425003D8557 /* Sources */,
 				19ECD0CF232ED425003D8557 /* Frameworks */,
 				19ECD0D0232ED425003D8557 /* Resources */,
@@ -232,8 +231,8 @@
 		19ECD0CA232ED425003D8557 /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastSwiftUpdateCheck = 1300;
-				LastUpgradeCheck = 1320;
+				LastSwiftUpdateCheck = 1341;
+				LastUpgradeCheck = 1341;
 				ORGANIZATIONNAME = Microsoft;
 				TargetAttributes = {
 					19ECD0D1232ED425003D8557 = {
@@ -297,7 +296,7 @@
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXShellScriptBuildPhase section */
-		1936CB1C2768EC280085FD98 /* Validate manifest */ = {
+		1936CB1C2768EC280085FD98 /* Validate Manifest */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -306,33 +305,14 @@
 			);
 			inputPaths = (
 			);
-			name = "Validate manifest";
+			name = "Validate Manifest";
 			outputFileListPaths = (
 			);
 			outputPaths = (
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "if command -v node >/dev/null 2>&1; then\n  node -e 'require(\"react-native-test-app/scripts/validate-manifest\")'\nelse\n  echo 'Could not validate manifest because `node` was not found'\nfi\n";
-		};
-		196C722C2330D28A006556ED /* Run SwiftLint */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-			);
-			name = "Run SwiftLint";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "${PODS_ROOT}/SwiftLint/swiftlint || exit 0\n";
-			showEnvVarsInLog = 0;
+			shellScript = "source .env\nnode -e 'require(\"react-native-test-app/scripts/validate-manifest\").validate(\"file\")'\n";
 		};
 /* End PBXShellScriptBuildPhase section */
 
diff --git a/ios/ReactTestApp/Manifest+Decoder.swift b/ios/ReactTestApp/Manifest+Decoder.swift
index ea42a87..06d9926 100644
--- a/ios/ReactTestApp/Manifest+Decoder.swift
+++ b/ios/ReactTestApp/Manifest+Decoder.swift
@@ -1,5 +1,3 @@
-import CommonCrypto
-import CryptoKit
 import Foundation
 
 extension Component {
@@ -38,18 +36,22 @@ extension Component {
 
 extension Manifest {
     static func fromFile() -> (Manifest, String)? {
-        guard let manifestURL = Bundle.main.url(forResource: "app", withExtension: "json"),
-              let data = try? Data(contentsOf: manifestURL, options: .uncached)
-        else {
-            return nil
+        guard let appManifest = UnsafeMutableRawPointer(mutating: ReactTestApp_AppManifest) else {
+            fatalError()
         }
+
+        let data = Data(
+            bytesNoCopy: appManifest,
+            count: ReactTestApp_AppManifestLength,
+            deallocator: .none
+        )
         return from(data: data)
     }
 
     static func from(data: Data) -> (Manifest, String)? {
         do {
             let manifest = try JSONDecoder().decode(self, from: data)
-            return (manifest, data.sha256)
+            return (manifest, String(cString: ReactTestApp_AppManifestChecksum))
         } catch {
             assertionFailure("Failed to load manifest: \(error)")
             return nil
@@ -107,24 +109,6 @@ extension Array where Element == Any {
     }
 }
 
-extension Data {
-    var sha256: String {
-        guard #available(iOS 13.0, macOS 10.15, *) else {
-            var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
-            _ = withUnsafeBytes {
-                CC_SHA256($0.baseAddress, UInt32(count), &digest)
-            }
-
-            return digest.reduce("") { str, byte in
-                str + String(format: "%02x", UInt8(byte))
-            }
-        }
-
-        let digest = SHA256.hash(data: self)
-        return Array(digest.makeIterator()).map { String(format: "%02x", $0) }.joined()
-    }
-}
-
 extension Dictionary where Key == AnyHashable, Value == Any {
     static func decode(from decoder: Decoder) throws -> Dictionary? {
         guard let container = try? decoder.container(keyedBy: DynamicCodingKey.self) else {
diff --git a/ios/ReactTestApp/ReactTestApp-Bridging-Header.h b/ios/ReactTestApp/ReactTestApp-Bridging-Header.h
index 57b484e..97ae524 100644
--- a/ios/ReactTestApp/ReactTestApp-Bridging-Header.h
+++ b/ios/ReactTestApp/ReactTestApp-Bridging-Header.h
@@ -28,3 +28,6 @@
 #import "React+Compatibility.h"
 #import "React+Fabric.h"
 #import "UIViewController+ReactTestApp.h"
+
+// Generated by `validate-manifest.js`
+#import "app.json.h"
diff --git a/ios/test_app.rb b/ios/test_app.rb
index fbaf65c..a5a5c77 100644
--- a/ios/test_app.rb
+++ b/ios/test_app.rb
@@ -122,7 +122,7 @@ def resources_pod(project_root, target_platform, platforms)
       'ios' => platforms[:ios],
       'osx' => platforms[:macos],
     },
-    'resources' => ['app.json', *resources],
+    'resources' => [*resources],
   }
 
   podspec_path = File.join(app_dir, 'ReactTestApp-Resources.podspec.json')
@@ -208,6 +208,13 @@ def make_project!(xcodeproj, project_root, target_platform, options)
     end
   end
 
+  # Note the location of Node so we can use it later in script phases
+  File.open(File.join(destination, '.env'), 'w') do |f|
+    node_bin = `dirname $(which node)`
+    node_bin.strip!
+    f.write("export PATH=#{node_bin}:$PATH\n")
+  end
+
   react_native = react_native_path(project_root, target_platform)
   rn_version = package_version(react_native.to_s).segments
   rn_version = (rn_version[0] * 10_000) + (rn_version[1] * 100) + rn_version[2]
@@ -278,6 +285,9 @@ def make_project!(xcodeproj, project_root, target_platform, options)
         if single_app.is_a? String
           config.build_settings['OTHER_SWIFT_FLAGS'] << '-DENABLE_SINGLE_APP_MODE'
         end
+
+        config.build_settings['USER_HEADER_SEARCH_PATHS'] ||= []
+        config.build_settings['USER_HEADER_SEARCH_PATHS'] << File.dirname(destination)
       end
     when 'ReactTestAppTests'
       target.build_configurations.each do |config|
@@ -327,8 +337,6 @@ def use_test_app_internal!(target_platform, options)
   react_native_post_install = nil
 
   target 'ReactTestApp' do
-    pod 'SwiftLint'
-
     react_native_post_install = use_react_native!(project_root, target_platform, options)
 
     if (resources_pod_path = resources_pod(project_root, target_platform, platforms))
diff --git a/macos/ReactTestApp.xcodeproj/project.pbxproj b/macos/ReactTestApp.xcodeproj/project.pbxproj
index 4a14bed..b0c3fb3 100644
--- a/macos/ReactTestApp.xcodeproj/project.pbxproj
+++ b/macos/ReactTestApp.xcodeproj/project.pbxproj
@@ -167,8 +167,7 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 193EF085247A736300BE8C79 /* Build configuration list for PBXNativeTarget "ReactTestApp" */;
 			buildPhases = (
-				19E2AE7C27693278007B06CA /* Validate manifest */,
-				19482473248280F7001B8381 /* Run SwiftLint */,
+				19E2AE7C27693278007B06CA /* Validate Manifest */,
 				193EF05B247A736100BE8C79 /* Sources */,
 				193EF05C247A736100BE8C79 /* Frameworks */,
 				193EF05D247A736100BE8C79 /* Resources */,
@@ -224,8 +223,8 @@
 		193EF057247A736100BE8C79 /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastSwiftUpdateCheck = 1300;
-				LastUpgradeCheck = 1320;
+				LastSwiftUpdateCheck = 1341;
+				LastUpgradeCheck = 1341;
 				ORGANIZATIONNAME = Microsoft;
 				TargetAttributes = {
 					193EF05E247A736100BE8C79 = {
@@ -291,7 +290,7 @@
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXShellScriptBuildPhase section */
-		19482473248280F7001B8381 /* Run SwiftLint */ = {
+		19E2AE7C27693278007B06CA /* Validate Manifest */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
@@ -300,32 +299,14 @@
 			);
 			inputPaths = (
 			);
-			name = "Run SwiftLint";
+			name = "Validate Manifest";
 			outputFileListPaths = (
 			);
 			outputPaths = (
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "${PODS_ROOT}/SwiftLint/swiftlint || exit 0\n";
-		};
-		19E2AE7C27693278007B06CA /* Validate manifest */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-			);
-			name = "Validate manifest";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "if command -v node >/dev/null 2>&1; then\n  node -e 'require(\"react-native-test-app/scripts/validate-manifest\")'\nelse\n  echo 'Could not validate manifest because `node` was not found'\nfi\n";
+			shellScript = "source .env\nnode -e 'require(\"react-native-test-app/scripts/validate-manifest\").validate(\"file\")'\n";
 		};
 /* End PBXShellScriptBuildPhase section */
 
diff --git a/test-app.gradle b/test-app.gradle
index 53665c2..7c4a282 100644
--- a/test-app.gradle
+++ b/test-app.gradle
@@ -101,36 +101,7 @@ ext.applyTestAppModule = { Project project ->
     def generatedRawDir = file("${generatedResDir}/raw")
     generatedRawDir.mkdirs()
 
-    def validateManifest = tasks.register("validateManifest", Exec) {
-        workingDir(projectRoot)
-
-        if (Os.isFamily(Os.FAMILY_WINDOWS)) {
-            commandLine("node.exe", "-e", "\"require('react-native-test-app/scripts/validate-manifest')\"")
-        } else {
-            def result = exec {
-                commandLine("bash", "-l", "-c", "command -v node")
-                ignoreExitValue = true
-                standardOutput = new ByteArrayOutputStream()
-            }
-            if (result.getExitValue() == 0) {
-                commandLine("node", "-e", "require('react-native-test-app/scripts/validate-manifest')")
-            } else {
-                logger.warn("Could not validate manifest because `node` was not found")
-                commandLine("true")
-            }
-        }
-    }
-
-    preBuild.dependsOn(tasks.register("copyManifest", Copy) {
-        dependsOn(validateManifest)
-
-        from(manifestFile)
-        into(generatedRawDir)
-    })
-
     preBuild.dependsOn(tasks.register("copyAssets", Copy) {
-        dependsOn(validateManifest)
-
         androidResourceFiles.each {
             from(it)
         }
@@ -139,8 +110,6 @@ ext.applyTestAppModule = { Project project ->
     })
 
     preBuild.dependsOn(tasks.register("copyResources", Copy) {
-        dependsOn(validateManifest)
-
         if (androidResDir != null) {
             from(androidResDir)
             into(generatedResDir)
diff --git a/test/test_test_app.rb b/test/test_test_app.rb
index 94d8324..988bd58 100644
--- a/test/test_test_app.rb
+++ b/test/test_test_app.rb
@@ -155,8 +155,8 @@ class TestTestApp < Minitest::Test
       GC.disable
 
       platforms = { :ios => '14.0', :macos => '11.0' }
-      resources = %w[app.json dist/assets dist/main.jsbundle]
-      platform_resources = ['app.json', "dist-#{target}/assets", "dist-#{target}/main.jsbundle"]
+      resources = %w[dist/assets dist/main.jsbundle]
+      platform_resources = ["dist-#{target}/assets", "dist-#{target}/main.jsbundle"]
 
       [
         fixture_path('with_platform_resources'),
@@ -173,7 +173,7 @@ class TestTestApp < Minitest::Test
         manifest = JSON.parse(File.read(manifest_path))
 
         if project_root.to_s.include?('without')
-          assert_equal(['app.json'], manifest['resources'])
+          assert_equal([], manifest['resources'])
         elsif project_root.to_s.include?('with_platform_resources')
           assert_equal(platform_resources, manifest['resources'].sort)
         else
diff --git a/test/validate-manifest.test.js b/test/validate-manifest.test.js
index e76a361..45b1ed7 100644
--- a/test/validate-manifest.test.js
+++ b/test/validate-manifest.test.js
@@ -6,7 +6,7 @@ jest.mock("fs");
 describe("validate-manifest", () => {
   const { mockFiles } = require("./mockFiles");
   const {
-    findAppManifest,
+    findFile,
     validateManifest,
   } = require("../scripts/validate-manifest");
 
@@ -31,15 +31,15 @@ describe("validate-manifest", () => {
       "example/node_modules/react-native-test-app/package.json": `{ "name": "Example" }`,
     });
 
-    expect(findAppManifest()).toBeUndefined();
-    expect(findAppManifest("example")).toBeDefined();
+    expect(findFile("app.json")).toBeUndefined();
+    expect(findFile("app.json", "example")).toBeDefined();
     expect(
-      findAppManifest("example/node_modules/react-native-test-app")
+      findFile("app.json", "example/node_modules/react-native-test-app")
     ).toBeDefined();
   });
 
   test("handles missing app manifest", () => {
-    expect(() => validateManifest(undefined)).toThrow("1");
+    expect(validateManifest(undefined)).toBe(1);
     expect(consoleSpy).toHaveBeenCalledTimes(1);
     expect(consoleSpy).toHaveBeenCalledWith(
       `Failed to find 'app.json'. Please make sure you're in the right directory.`
@@ -50,7 +50,7 @@ describe("validate-manifest", () => {
     mockFiles({
       "app.json": `{ "name": "Example" }`,
     });
-    expect(() => validateManifest()).toThrow("1");
+    expect(validateManifest(findFile("app.json"))).toBe(1001);
     expect(consoleSpy).toHaveBeenCalledTimes(2);
     expect(consoleSpy).toHaveBeenCalledWith(
       expect.stringContaining(
@@ -81,7 +81,7 @@ describe("validate-manifest", () => {
         ]
       }`,
     });
-    expect(() => validateManifest()).toThrow("1");
+    expect(validateManifest(findFile("app.json"))).toBe(1001);
     expect(consoleSpy).toHaveBeenCalledTimes(2);
     expect(consoleSpy).toHaveBeenCalledWith(
       expect.stringContaining(
@@ -108,7 +108,7 @@ describe("validate-manifest", () => {
         ]
       }`,
     });
-    expect(() => validateManifest()).toThrow("1");
+    expect(validateManifest(findFile("app.json"))).toBe(1001);
     expect(consoleSpy).toHaveBeenCalledTimes(2);
     expect(consoleSpy).toHaveBeenCalledWith(
       expect.stringContaining(
@@ -130,7 +130,7 @@ describe("validate-manifest", () => {
         "resources": 0
       }`,
     });
-    expect(() => validateManifest()).toThrow("1");
+    expect(validateManifest(findFile("app.json"))).toBe(1003);
     expect(consoleSpy).toHaveBeenCalledWith(
       expect.stringContaining(
         "app.json: error: app.json is not a valid app manifest"
@@ -155,7 +155,7 @@ describe("validate-manifest", () => {
         ]
       }`,
     });
-    expect(() => validateManifest()).toThrow("1");
+    expect(validateManifest(findFile("app.json"))).toBe(1003);
     expect(consoleSpy).toHaveBeenCalledWith(
       expect.stringContaining(
         "app.json: error: app.json is not a valid app manifest"
@@ -184,7 +184,7 @@ describe("validate-manifest", () => {
         }`,
       });
 
-      expect(() => validateManifest()).toThrow("1");
+      expect(validateManifest(findFile("app.json"))).toBe(1003);
       expect(consoleSpy).toHaveBeenCalledWith(
         expect.stringContaining(
           "app.json: error: app.json is not a valid app manifest"
@@ -237,7 +237,23 @@ describe("validate-manifest", () => {
         }
       }`,
     });
-    expect(() => validateManifest()).not.toThrow();
+    expect(validateManifest(findFile("app.json"))).toMatchInlineSnapshot(`
+      Object {
+        "components": Array [
+          Object {
+            "appKey": "Example",
+            "displayName": "App",
+          },
+          Object {
+            "appKey": "Example",
+            "displayName": "App (modal)",
+            "presentationStyle": "modal",
+          },
+        ],
+        "displayName": "Example",
+        "name": "Example",
+      }
+    `);
     expect(consoleSpy).not.toHaveBeenCalled();
   });
 });
diff --git a/test/windows-test-app/getBundleResources.test.js b/test/windows-test-app/getBundleResources.test.js
index 09f385d..965aa68 100644
--- a/test/windows-test-app/getBundleResources.test.js
+++ b/test/windows-test-app/getBundleResources.test.js
@@ -33,12 +33,18 @@ describe("getBundleResources", () => {
     expect(appName).toBe("Example");
     expect(appxManifest).toBe("windows\\Package.appxmanifest");
     expect(assetItems).toMatchInlineSnapshot(`
-      "<CopyFileToFolders Include=\\"$(ProjectRootDir)\\\\dist\\\\main.bundle\\">
+      "<CopyFileToFolders Include=\\"$(ProjectRootDir)\\\\dist\\\\assets\\\\app.json\\">
+            <DestinationFolders>$(OutDir)\\\\Bundle\\\\assets</DestinationFolders>
+          </CopyFileToFolders>
+          <CopyFileToFolders Include=\\"$(ProjectRootDir)\\\\dist\\\\main.bundle\\">
             <DestinationFolders>$(OutDir)\\\\Bundle</DestinationFolders>
           </CopyFileToFolders>"
     `);
     expect(assetItemFilters).toMatchInlineSnapshot(`
-      "<CopyFileToFolders Include=\\"$(ProjectRootDir)\\\\dist\\\\main.bundle\\">
+      "<CopyFileToFolders Include=\\"$(ProjectRootDir)\\\\dist\\\\assets\\\\app.json\\">
+            <Filter>Assets\\\\assets</Filter>
+          </CopyFileToFolders>
+          <CopyFileToFolders Include=\\"$(ProjectRootDir)\\\\dist\\\\main.bundle\\">
             <Filter>Assets</Filter>
           </CopyFileToFolders>"
     `);
diff --git a/test/windows-test-app/parseResources.test.js b/test/windows-test-app/parseResources.test.js
index 799ea2f..6b8ae21 100644
--- a/test/windows-test-app/parseResources.test.js
+++ b/test/windows-test-app/parseResources.test.js
@@ -20,7 +20,6 @@ describe("parseResources", () => {
 
   test("returns references to existing assets", () => {
     mockFiles({
-      "dist/assets/app.json": "{}",
       "dist/assets/node_modules/arnold/portrait.png": "{}",
       "dist/assets/splash.png": "{}",
       "dist/main.jsbundle": "'use strict';",
diff --git a/windows/ReactTestApp/MainPage.cpp b/windows/ReactTestApp/MainPage.cpp
index ebc7ade..404ce81 100644
--- a/windows/ReactTestApp/MainPage.cpp
+++ b/windows/ReactTestApp/MainPage.cpp
@@ -155,7 +155,7 @@ MainPage::MainPage()
     InitializeComponent();
     InitializeTitleBar();
 
-    auto result = ::ReactTestApp::GetManifest("app.json");
+    auto result = ::ReactTestApp::GetManifest();
     if (result.has_value()) {
         auto &[manifest, checksum] = result.value();
 
diff --git a/windows/ReactTestApp/Manifest.cpp b/windows/ReactTestApp/Manifest.cpp
index 6eae6e2..14f8495 100644
--- a/windows/ReactTestApp/Manifest.cpp
+++ b/windows/ReactTestApp/Manifest.cpp
@@ -2,29 +2,14 @@
 
 #include "Manifest.h"
 
-#include <cstdio>
-
 #include <nlohmann/json.hpp>
-#include <winrt/Windows.Security.Cryptography.Core.h>
-#include <winrt/Windows.Security.Cryptography.h>
+
+#include "app.json.h"
 
 using nlohmann::detail::value_t;
-using winrt::Windows::Security::Cryptography::BinaryStringEncoding;
-using winrt::Windows::Security::Cryptography::CryptographicBuffer;
-using winrt::Windows::Security::Cryptography::Core::HashAlgorithmNames;
-using winrt::Windows::Security::Cryptography::Core::HashAlgorithmProvider;
 
 namespace
 {
-    std::string checksum(std::string const &data)
-    {
-        auto hasher = HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha256());
-        auto digest = hasher.HashData(CryptographicBuffer::ConvertStringToBinary(
-            winrt::to_hstring(data), BinaryStringEncoding::Utf8));
-        auto checksum = CryptographicBuffer::EncodeToHexString(digest);
-        return winrt::to_string(checksum);
-    }
-
     template <typename T>
     std::optional<T> get_optional(const nlohmann::json &j, const std::string &key)
     {
@@ -110,26 +95,14 @@ namespace ReactTestApp
                            .value_or(std::vector<Component>{});
     }
 
-    std::optional<std::tuple<Manifest, std::string>> GetManifest(std::string const &filename)
+    std::optional<std::tuple<Manifest, std::string>> GetManifest(const char *const json)
     {
-        std::FILE *stream = nullptr;
-        if (fopen_s(&stream, filename.c_str(), "rb") != 0) {
-            return std::nullopt;
-        }
-
-        std::string json;
-        std::fseek(stream, 0, SEEK_END);
-        json.resize(std::ftell(stream));
-
-        std::rewind(stream);
-        std::fread(json.data(), 1, json.size(), stream);
-        std::fclose(stream);
-
-        auto j = nlohmann::json::parse(json, nullptr, false);
+        auto manifest = json == nullptr ? ReactTestApp_AppManifest : json;
+        auto j = nlohmann::json::parse(manifest, nullptr, false);
         if (j.is_discarded()) {
             return std::nullopt;
         }
 
-        return std::make_tuple(j.get<Manifest>(), checksum(json));
+        return std::make_tuple(j.get<Manifest>(), std::string{ReactTestApp_AppManifestChecksum});
     }
 }  // namespace ReactTestApp
diff --git a/windows/ReactTestApp/Manifest.h b/windows/ReactTestApp/Manifest.h
index eb1b707..22554c5 100644
--- a/windows/ReactTestApp/Manifest.h
+++ b/windows/ReactTestApp/Manifest.h
@@ -30,7 +30,7 @@ namespace ReactTestApp
         std::optional<std::vector<Component>> components;
     };
 
-    std::optional<std::tuple<Manifest, std::string>> GetManifest(std::string const &filename);
+    std::optional<std::tuple<Manifest, std::string>> GetManifest(const char *const json = nullptr);
 
 }  // namespace ReactTestApp
 
diff --git a/windows/ReactTestApp/ReactTestApp.vcxproj b/windows/ReactTestApp/ReactTestApp.vcxproj
index eccabcb..1acf9c3 100644
--- a/windows/ReactTestApp/ReactTestApp.vcxproj
+++ b/windows/ReactTestApp/ReactTestApp.vcxproj
@@ -25,7 +25,9 @@
   <PropertyGroup Label="ReactNativeWindowsProps">
     <ProjectRootDir Condition="'$(ProjectRootDir)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'app.json'))</ProjectRootDir>
     <ReactNativeWindowsDir Condition="'$(ReactNativeWindowsDir)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'node_modules\react-native-windows\package.json'))\node_modules\react-native-windows\</ReactNativeWindowsDir>
-    <ReactTestAppDir Condition="'$(ReactTestAppDir)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'node_modules\react-native-test-app\package.json'))\node_modules\react-native-test-app\windows\ReactTestApp\</ReactTestAppDir>
+    <ReactTestAppDir Condition="'$(ReactTestAppDir)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'node_modules\react-native-test-app\package.json'))\node_modules\react-native-test-app\windows\ReactTestApp</ReactTestAppDir>
+    <ReactTestAppCommonDir Condition="'$(ReactTestAppCommonDir)' == ''">$(ReactTestAppDir)\..\..\common</ReactTestAppCommonDir>
+    <ReactTestAppGeneratedDir Condition="'$(ReactTestAppGeneratedDir)' == ''">$(MSBuildProjectDirectory)\..\..</ReactTestAppGeneratedDir>
     <WinUI2xVersionDisabled />
   </PropertyGroup>
   <Import Project="$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.WindowsSdk.Default.props" Condition="Exists('$(ReactNativeWindowsDir)\PropertySheets\External\Microsoft.ReactNative.WindowsSdk.Default.props')" />
@@ -92,28 +94,28 @@
     <Import Project="$(SolutionDir)packages\$(WinUIPackageProps)" Condition="'$(WinUIPackageProps)' != '' And Exists('$(SolutionDir)packages\$(WinUIPackageProps)')" />
   </ImportGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
-    <IncludePath>$(ReactTestAppDir);$(ReactTestAppDir)\..\..\common;$(IncludePath)</IncludePath>
+    <IncludePath>$(ReactTestAppDir);$(ReactTestAppCommonDir);$(ReactTestAppGeneratedDir);$(IncludePath)</IncludePath>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
-    <IncludePath>$(ReactTestAppDir);$(ReactTestAppDir)\..\..\common;$(IncludePath)</IncludePath>
+    <IncludePath>$(ReactTestAppDir);$(ReactTestAppCommonDir);$(ReactTestAppGeneratedDir);$(IncludePath)</IncludePath>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
-    <IncludePath>$(ReactTestAppDir);$(ReactTestAppDir)\..\..\common;$(IncludePath)</IncludePath>
+    <IncludePath>$(ReactTestAppDir);$(ReactTestAppCommonDir);$(ReactTestAppGeneratedDir);$(IncludePath)</IncludePath>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
-    <IncludePath>$(ReactTestAppDir);$(ReactTestAppDir)\..\..\common;$(IncludePath)</IncludePath>
+    <IncludePath>$(ReactTestAppDir);$(ReactTestAppCommonDir);$(ReactTestAppGeneratedDir);$(IncludePath)</IncludePath>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <IncludePath>$(ReactTestAppDir);$(ReactTestAppDir)\..\..\common;$(IncludePath)</IncludePath>
+    <IncludePath>$(ReactTestAppDir);$(ReactTestAppCommonDir);$(ReactTestAppGeneratedDir);$(IncludePath)</IncludePath>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
-    <IncludePath>$(ReactTestAppDir);$(ReactTestAppDir)\..\..\common;$(IncludePath)</IncludePath>
+    <IncludePath>$(ReactTestAppDir);$(ReactTestAppCommonDir);$(ReactTestAppGeneratedDir);$(IncludePath)</IncludePath>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
-    <IncludePath>$(ReactTestAppDir);$(ReactTestAppDir)\..\..\common;$(IncludePath)</IncludePath>
+    <IncludePath>$(ReactTestAppDir);$(ReactTestAppCommonDir);$(ReactTestAppGeneratedDir);$(IncludePath)</IncludePath>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
-    <IncludePath>$(ReactTestAppDir);$(ReactTestAppDir)\..\..\common;$(IncludePath)</IncludePath>
+    <IncludePath>$(ReactTestAppDir);$(ReactTestAppCommonDir);$(ReactTestAppGeneratedDir);$(IncludePath)</IncludePath>
   </PropertyGroup>
   <ItemDefinitionGroup>
     <ClCompile>
@@ -145,7 +147,7 @@
     <ClInclude Include="$(ReactTestAppDir)\App.h">
       <DependentUpon>$(ReactTestAppDir)\App.xaml</DependentUpon>
     </ClInclude>
-    <ClInclude Include="$(ReactTestAppDir)\..\..\common\AppRegistry.h" />
+    <ClInclude Include="$(ReactTestAppCommonDir)\AppRegistry.h" />
     <ClInclude Include="$(ReactTestAppDir)\AutolinkedNativeModules.g.h" />
     <ClInclude Include="$(ReactTestAppDir)\MainPage.h">
       <DependentUpon>$(ReactTestAppDir)\MainPage.xaml</DependentUpon>
@@ -178,7 +180,6 @@
     <Image Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
     <Image Include="Assets\SplashScreen.scale-200.png" />
     <Image Include="Assets\StoreLogo.png" />
-    <Text Include="$(ProjectRootDir)\app.json" />
     <!-- ReactTestApp asset items -->
   </ItemGroup>
   <ItemGroup>
@@ -188,7 +189,7 @@
     <ClCompile Include="$(ReactTestAppDir)\App.cpp">
       <DependentUpon>$(ReactTestAppDir)\App.xaml</DependentUpon>
     </ClCompile>
-    <ClCompile Include="$(ReactTestAppDir)\..\..\common\AppRegistry.cpp">
+    <ClCompile Include="$(ReactTestAppCommonDir)\AppRegistry.cpp">
       <PrecompiledHeader>NotUsing</PrecompiledHeader>
     </ClCompile>
     <ClCompile Include="AutolinkedNativeModules.g.cpp" />
@@ -209,6 +210,7 @@
     </Midl>
   </ItemGroup>
   <ItemGroup>
+    <None Include="$(ProjectRootDir)\app.json" />
     <None Include="packages.config" />
     <None Include="PropertySheet.props" />
   </ItemGroup>
diff --git a/windows/ReactTestApp/ReactTestApp.vcxproj.filters b/windows/ReactTestApp/ReactTestApp.vcxproj.filters
index 2838efb..a408643 100644
--- a/windows/ReactTestApp/ReactTestApp.vcxproj.filters
+++ b/windows/ReactTestApp/ReactTestApp.vcxproj.filters
@@ -13,7 +13,7 @@
   <ItemGroup>
     <ClCompile Include="$(ReactTestAppDir)\pch.cpp" />
     <ClCompile Include="$(ReactTestAppDir)\App.cpp" />
-    <ClCompile Include="$(ReactTestAppDir)\..\..\common\AppRegistry.cpp" />
+    <ClCompile Include="$(ReactTestAppCommonDir)\AppRegistry.cpp" />
     <ClCompile Include="$(ProjectDir)\AutolinkedNativeModules.g.cpp" />
     <ClCompile Include="$(ReactTestAppDir)\MainPage.cpp" />
     <ClCompile Include="$(ReactTestAppDir)\Manifest.cpp" />
@@ -24,7 +24,7 @@
   <ItemGroup>
     <ClInclude Include="$(ReactTestAppDir)\pch.h" />
     <ClInclude Include="$(ReactTestAppDir)\App.h" />
-    <ClInclude Include="$(ReactTestAppDir)\..\..\common\AppRegistry.h" />
+    <ClInclude Include="$(ReactTestAppCommonDir)\AppRegistry.h" />
     <ClInclude Include="$(ReactTestAppDir)\AutolinkedNativeModules.g.h" />
     <ClInclude Include="$(ReactTestAppDir)\MainPage.h" />
     <ClInclude Include="$(ReactTestAppDir)\Manifest.h" />
@@ -51,9 +51,6 @@
     <Image Include="Assets\StoreLogo.png">
       <Filter>Assets</Filter>
     </Image>
-    <Text Include="$(ProjectRootDir)\app.json">
-      <Filter>Assets</Filter>
-    </Text>
     <!-- ReactTestApp asset item filters -->
   </ItemGroup>
   <ItemGroup>
@@ -67,6 +64,7 @@
     <!-- ReactTestApp asset filters -->
   </ItemGroup>
   <ItemGroup>
+    <None Include="$(ProjectRootDir)\app.json" />
     <None Include="PropertySheet.props" />
     <None Include="packages.config" />
   </ItemGroup>
diff --git a/windows/ReactTestApp/ValidateManifest.targets b/windows/ReactTestApp/ValidateManifest.targets
index 3988055..e5e35a9 100644
--- a/windows/ReactTestApp/ValidateManifest.targets
+++ b/windows/ReactTestApp/ValidateManifest.targets
@@ -2,7 +2,7 @@
 <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <PropertyGroup>
     <DisableFastUpToDateCheck>True</DisableFastUpToDateCheck>
-    <ValidateManifestCommand>node -e "require('react-native-test-app/scripts/validate-manifest')"</ValidateManifestCommand>
+    <ValidateManifestCommand>node -e "require('react-native-test-app/scripts/validate-manifest').validate('file')"</ValidateManifestCommand>
   </PropertyGroup>
   <Target Name="ValidateManifest" BeforeTargets="PrepareForBuild">
     <Exec Command="$(ValidateManifestCommand)" WorkingDirectory="$(SolutionDir)" />
diff --git a/windows/test-app.js b/windows/test-app.js
index 52383d1..372f5e9 100755
--- a/windows/test-app.js
+++ b/windows/test-app.js
@@ -208,11 +208,6 @@ function generateContentItems(
 ) {
   const { assetFilters, assetItemFilters, assetItems } = assets;
   for (const resource of resources) {
-    if (path.basename(resource) === "app.json") {
-      // `app.json` is always included
-      continue;
-    }
-
     const resourcePath = path.isAbsolute(resource)
       ? path.relative(projectPath, resource)
       : resource;
@@ -541,6 +536,8 @@ function generateSolution(destPath, { autolink, useHermes, useNuGet }) {
   fs.mkdirSync(projectFilesDestPath, { recursive: true });
   fs.mkdirSync(destPath, { recursive: true });
 
+  require("../scripts/validate-manifest").validate("file");
+
   const manifestFilePath = findNearest("app.json", destPath);
   const {
     appName,