react-nitro-cli 0.0.0-stage → 0.0.1-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Margelo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9
+ of the Software, and to permit persons to whom the Software is furnished to do
10
+ so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,45 @@
1
- # Temporary Holding Version
1
+ # React Nitro CLI
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Prepare, build, and run React Nitro apps. This package owns the native host and build integration. Application components and `registerApp` come from `react-nitro-renderer`. This alpha requires an Apple Silicon Mac, Bun 1.4.2 or newer, and Xcode 27. Apps target iOS 16 or newer.
4
+
5
+ ```sh
6
+ bunx create-react-nitro MyApp
7
+ cd MyApp
8
+ bun run ios --device <simulator-UDID>
9
+ ```
10
+
11
+ Use `xcrun simctl list devices booted` to find a running simulator. You can also open `.nitro/ios/ReactNitro.xcodeproj` in Xcode, select a simulator, and run the app.
12
+
13
+ ## App files
14
+
15
+ - `src/App.tsx` contains your UI.
16
+ - `src/index.tsx` imports `registerApp` from the renderer and calls `registerApp(App)`. The renderer owns runtime and window setup.
17
+ - `app.json` defines the app name and bundle identifier. Optional fields are `displayName`, `entry`, `version`, `buildNumber`, and `developmentTeam`.
18
+ - `tsconfig.json` extends this package's application configuration.
19
+
20
+ The native host, Swift packages, Xcode settings, and bundle build phase are generated under `.nitro/`. That directory is disposable and should stay out of version control. Configure the app through `app.json`; changes made inside `.nitro/` are overwritten.
21
+
22
+ ## Upgrade
23
+
24
+ ```sh
25
+ bun add react-nitro-cli@latest react-nitro-renderer@latest
26
+ ```
27
+
28
+ Update the CLI and renderer together. The app's `postinstall` runs `react-nitro prepare`, which refreshes the generated native integration while preserving your app files. All framework packages use matching exact versions, including the pinned upstream `nitro-core` dependency used for both JavaScript and native code. There are no source symlinks to maintain.
29
+
30
+ After editing `app.json`, run `bun run react-nitro prepare`, or run `bun run ios` to prepare and build together. To recreate native sources from scratch, use `bun run react-nitro prepare --clean`.
31
+
32
+ ## Builds
33
+
34
+ ```sh
35
+ bun run ios --build-only
36
+ bun run ios --build-only --release
37
+ bun run ios --device <simulator-UDID> --release
38
+ bun run typecheck
39
+ ```
40
+
41
+ Debug builds bundle JavaScript source. Release builds compile it to bytecode with the matching, included Hermes compiler. Building an app does not require CMake, Ninja, Nitrogen, or FFI generation. Xcode compiles the shipped native sources, and SwiftPM resolves the SimdUTF dependency.
42
+
43
+ This alpha loads the bundled JavaScript file. It does not provide Fast Refresh or Android support yet.
44
+
45
+ Import components, rendering functions, and `registerApp` from `react-nitro-renderer`. Import navigation from `react-nitro-navigation`. The CLI has no application API exports.
@@ -0,0 +1,48 @@
1
+ import ReactNitroRenderer
2
+ import ReactNitroRuntime
3
+ import UIKit
4
+
5
+ @main
6
+ final class AppDelegate: UIResponder, UIApplicationDelegate, RuntimeReloadableAppDelegate {
7
+ private var runtime = JSRuntime()
8
+
9
+ func application(
10
+ _ application: UIApplication,
11
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
12
+ ) -> Bool {
13
+ runJavaScript()
14
+ return true
15
+ }
16
+
17
+ func application(
18
+ _ application: UIApplication,
19
+ configurationForConnecting connectingSceneSession: UISceneSession,
20
+ options: UIScene.ConnectionOptions
21
+ ) -> UISceneConfiguration {
22
+ return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
23
+ }
24
+
25
+ func reloadRuntime() {
26
+ DispatchQueue.main.async {
27
+ self.runtime = JSRuntime()
28
+ self.runJavaScript()
29
+ for scene in UIApplication.shared.connectedScenes {
30
+ guard let delegate = scene.delegate as? SceneDelegate else { continue }
31
+ delegate.mountRoot(in: scene)
32
+ }
33
+ }
34
+ }
35
+
36
+ private func runJavaScript() {
37
+ #if DEBUG
38
+ let file = "main.js"
39
+ #else
40
+ let file = "main.hbc"
41
+ #endif
42
+ do {
43
+ _ = try runtime.runJavaScript(fromFile: file)
44
+ } catch {
45
+ fatalError("Could not start React Nitro: \(error)")
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,27 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>CFBundleDisplayName</key>
6
+ <string>__DISPLAY_NAME__</string>
7
+ <key>UIApplicationSceneManifest</key>
8
+ <dict>
9
+ <key>UIApplicationSupportsMultipleScenes</key>
10
+ <false/>
11
+ <key>UISceneConfigurations</key>
12
+ <dict>
13
+ <key>UIWindowSceneSessionRoleApplication</key>
14
+ <array>
15
+ <dict>
16
+ <key>UISceneConfigurationName</key>
17
+ <string>Default Configuration</string>
18
+ <key>UISceneDelegateClassName</key>
19
+ <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
20
+ </dict>
21
+ </array>
22
+ </dict>
23
+ </dict>
24
+ <key>UILaunchScreen</key>
25
+ <dict/>
26
+ </dict>
27
+ </plist>
@@ -0,0 +1,25 @@
1
+ import ReactNitroRenderer
2
+ import UIKit
3
+
4
+ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
5
+ var window: UIWindow?
6
+
7
+ func scene(
8
+ _ scene: UIScene,
9
+ willConnectTo session: UISceneSession,
10
+ options connectionOptions: UIScene.ConnectionOptions
11
+ ) {
12
+ mountRoot(in: scene)
13
+ }
14
+
15
+ func mountRoot(in scene: UIScene) {
16
+ guard let windowScene = scene as? UIWindowScene else { return }
17
+ do {
18
+ let window = try RegisterRootCallback.onSceneCreated(windowScene)
19
+ window.makeKeyAndVisible()
20
+ self.window = window
21
+ } catch {
22
+ fatalError("Could not mount React Nitro: \(error)")
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,402 @@
1
+ // !$*UTF8*$!
2
+ {
3
+ archiveVersion = 1;
4
+ classes = {
5
+ };
6
+ objectVersion = 110;
7
+ objects = {
8
+
9
+ /* Begin PBXBuildFile section */
10
+ B8439CA6305C47AC00534242 /* ReactNitroRenderer in Frameworks */ = {isa = PBXBuildFile; productRef = B8439CA5305C47AC00534242 /* ReactNitroRenderer */; };
11
+ B85AAA02305B23CA002C8DF6 /* ReactNitroRuntime in Frameworks */ = {isa = PBXBuildFile; productRef = B85AAA01305B23CA002C8DF6 /* ReactNitroRuntime */; };
12
+ B8655EB9305D6D7D00F54F82 /* ReactNitroNetworking in Frameworks */ = {isa = PBXBuildFile; productRef = B8655EB8305D6D7D00F54F82 /* ReactNitroNetworking */; };
13
+ /* End PBXBuildFile section */
14
+
15
+ /* Begin PBXFileReference section */
16
+ AABBCCDD0000000000000001 = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = App.xcconfig; sourceTree = "<group>"; };
17
+ B85AA9E8305B1D50002C8DF6 /* ReactNitro.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ReactNitro.app"; sourceTree = BUILT_PRODUCTS_DIR; };
18
+ /* End PBXFileReference section */
19
+
20
+ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
21
+ B85AA9FA305B1D52002C8DF6 /* Exceptions for "ReactNitro" folder in "ReactNitro" target */ = {
22
+ isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
23
+ membershipExceptions = (
24
+ Info.plist,
25
+ );
26
+ target = B85AA9E7305B1D50002C8DF6 /* ReactNitro */;
27
+ };
28
+ /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
29
+
30
+ /* Begin PBXFileSystemSynchronizedRootGroup section */
31
+ B85AA9EA305B1D50002C8DF6 /* ReactNitro */ = {
32
+ isa = PBXFileSystemSynchronizedRootGroup;
33
+ exceptions = (
34
+ B85AA9FA305B1D52002C8DF6 /* Exceptions for "ReactNitro" folder in "ReactNitro" target */,
35
+ );
36
+ path = "ReactNitro";
37
+ sourceTree = "<group>";
38
+ };
39
+ /* End PBXFileSystemSynchronizedRootGroup section */
40
+
41
+ /* Begin PBXFrameworksBuildPhase section */
42
+ B85AA9E5305B1D50002C8DF6 /* Frameworks */ = {
43
+ isa = PBXFrameworksBuildPhase;
44
+ files = (
45
+ B85AAA02305B23CA002C8DF6 /* ReactNitroRuntime in Frameworks */,
46
+ B8439CA6305C47AC00534242 /* ReactNitroRenderer in Frameworks */,
47
+ B8655EB9305D6D7D00F54F82 /* ReactNitroNetworking in Frameworks */,
48
+ );
49
+ };
50
+ /* End PBXFrameworksBuildPhase section */
51
+
52
+ /* Begin PBXGroup section */
53
+ B85AA9DF305B1D50002C8DF6 = {
54
+ isa = PBXGroup;
55
+ children = (
56
+ B85AA9EA305B1D50002C8DF6 /* ReactNitro */,
57
+ B85AA9E9305B1D50002C8DF6 /* Products */,
58
+ );
59
+ sourceTree = "<group>";
60
+ };
61
+ B85AA9E9305B1D50002C8DF6 /* Products */ = {
62
+ isa = PBXGroup;
63
+ children = (
64
+ B85AA9E8305B1D50002C8DF6 /* ReactNitro.app */,
65
+ );
66
+ name = Products;
67
+ sourceTree = "<group>";
68
+ };
69
+ /* End PBXGroup section */
70
+
71
+ /* Begin PBXNativeTarget section */
72
+ B85AA9E7305B1D50002C8DF6 /* ReactNitro */ = {
73
+ isa = PBXNativeTarget;
74
+ buildConfigurationList = B85AA9FB305B1D52002C8DF6 /* Build configuration list for PBXNativeTarget "ReactNitro" */;
75
+ buildPhases = (
76
+ B85AA9E4305B1D50002C8DF6 /* Sources */,
77
+ B85AA9E5305B1D50002C8DF6 /* Frameworks */,
78
+ B85AA9E6305B1D50002C8DF6 /* Resources */,
79
+ B8F1E2D3307A1B2C00C4D5E6 /* Bundle JavaScript */,
80
+ );
81
+ buildRules = (
82
+ );
83
+ fileSystemSynchronizedGroups = (
84
+ B85AA9EA305B1D50002C8DF6 /* ReactNitro */,
85
+ );
86
+ name = "ReactNitro";
87
+ packageProductDependencies = (
88
+ B85AAA01305B23CA002C8DF6 /* ReactNitroRuntime */,
89
+ B8439CA5305C47AC00534242 /* ReactNitroRenderer */,
90
+ B8655EB8305D6D7D00F54F82 /* ReactNitroNetworking */,
91
+ );
92
+ productName = "ReactNitro";
93
+ productReference = B85AA9E8305B1D50002C8DF6 /* ReactNitro.app */;
94
+ productType = "com.apple.product-type.application";
95
+ };
96
+ /* End PBXNativeTarget section */
97
+
98
+ /* Begin PBXProject section */
99
+ B85AA9E0305B1D50002C8DF6 /* Project object */ = {
100
+ isa = PBXProject;
101
+ attributes = {
102
+ BuildIndependentTargetsInParallel = 1;
103
+ LastSwiftUpdateCheck = 2700;
104
+ LastUpgradeCheck = 2700;
105
+ TargetAttributes = {
106
+ B85AA9E7305B1D50002C8DF6 = {
107
+ CreatedOnToolsVersion = 27.0;
108
+ };
109
+ };
110
+ };
111
+ buildConfigurationList = B85AA9E3305B1D50002C8DF6 /* Build configuration list for PBXProject "ReactNitro" */;
112
+ developmentRegion = en;
113
+ hasScannedForEncodings = 0;
114
+ knownRegions = (
115
+ en,
116
+ Base,
117
+ );
118
+ mainGroup = B85AA9DF305B1D50002C8DF6;
119
+ minimizedProjectReferenceProxies = 1;
120
+ packageReferences = (
121
+ B85AAA00305B23CA002C8DF6 /* XCLocalSwiftPackageReference "../native/react-nitro-runtime" */,
122
+ B8439CA4305C47AC00534242 /* XCLocalSwiftPackageReference "../native/react-nitro-renderer" */,
123
+ B8655EB7305D6D7D00F54F82 /* XCLocalSwiftPackageReference "../native/react-nitro-networking" */,
124
+ );
125
+ preferredProjectObjectVersion = 77;
126
+ productRefGroup = B85AA9E9305B1D50002C8DF6 /* Products */;
127
+ projectDirPath = "";
128
+ projectRoot = "";
129
+ targets = (
130
+ B85AA9E7305B1D50002C8DF6 /* ReactNitro */,
131
+ );
132
+ };
133
+ /* End PBXProject section */
134
+
135
+ /* Begin PBXResourcesBuildPhase section */
136
+ B85AA9E6305B1D50002C8DF6 /* Resources */ = {
137
+ isa = PBXResourcesBuildPhase;
138
+ files = (
139
+ );
140
+ };
141
+ /* End PBXResourcesBuildPhase section */
142
+
143
+ /* Begin PBXShellScriptBuildPhase section */
144
+ B8F1E2D3307A1B2C00C4D5E6 /* Bundle JavaScript */ = {
145
+ isa = PBXShellScriptBuildPhase;
146
+ alwaysOutOfDate = 1;
147
+ buildActionMask = 2147483647;
148
+ files = (
149
+ );
150
+ inputFileListPaths = (
151
+ );
152
+ inputPaths = (
153
+ );
154
+ name = "Bundle JavaScript";
155
+ outputFileListPaths = (
156
+ );
157
+ outputPaths = (
158
+ );
159
+ runOnlyForDeploymentPostprocessing = 0;
160
+ shellPath = /bin/sh;
161
+ shellScript = "export PATH=\"$HOME/.bun/bin:/opt/homebrew/bin:/usr/local/bin:$PATH\"\nbun \"$NITRO_CLI_PATH\" bundle --root \"$NITRO_PROJECT_ROOT\" --xcode\n";
162
+ };
163
+ /* End PBXShellScriptBuildPhase section */
164
+
165
+ /* Begin PBXSourcesBuildPhase section */
166
+ B85AA9E4305B1D50002C8DF6 /* Sources */ = {
167
+ isa = PBXSourcesBuildPhase;
168
+ files = (
169
+ );
170
+ };
171
+ /* End PBXSourcesBuildPhase section */
172
+
173
+ /* Begin XCBuildConfiguration section */
174
+ B85AA9FC305B1D52002C8DF6 /* Debug configuration for PBXNativeTarget "ReactNitro" */ = {
175
+ isa = XCBuildConfiguration;
176
+ baseConfigurationReference = AABBCCDD0000000000000001;
177
+ buildSettings = {
178
+ CODE_SIGN_STYLE = Automatic;
179
+ GENERATE_INFOPLIST_FILE = YES;
180
+ INFOPLIST_FILE = "ReactNitro/Info.plist";
181
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
182
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
183
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
184
+ LD_RUNPATH_SEARCH_PATHS = (
185
+ "$(inherited)",
186
+ "@executable_path/Frameworks",
187
+ );
188
+ OTHER_SWIFT_FLAGS = "$(inherited) -Xfrontend -clang-header-expose-decls=has-expose-attr-or-stdlib";
189
+ PRODUCT_NAME = "$(TARGET_NAME)";
190
+ STRING_CATALOG_GENERATE_SYMBOLS = YES;
191
+ SWIFT_APPROACHABLE_CONCURRENCY = YES;
192
+ SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
193
+ SWIFT_EMIT_LOC_STRINGS = YES;
194
+ SWIFT_OBJC_INTEROP_MODE = objcxx;
195
+ SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
196
+ SWIFT_VERSION = 6.0;
197
+ TARGETED_DEVICE_FAMILY = "1,2";
198
+ };
199
+ name = Debug;
200
+ };
201
+ B85AA9FD305B1D52002C8DF6 /* Release configuration for PBXNativeTarget "ReactNitro" */ = {
202
+ isa = XCBuildConfiguration;
203
+ baseConfigurationReference = AABBCCDD0000000000000001;
204
+ buildSettings = {
205
+ CODE_SIGN_STYLE = Automatic;
206
+ GENERATE_INFOPLIST_FILE = YES;
207
+ INFOPLIST_FILE = "ReactNitro/Info.plist";
208
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
209
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
210
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
211
+ LD_RUNPATH_SEARCH_PATHS = (
212
+ "$(inherited)",
213
+ "@executable_path/Frameworks",
214
+ );
215
+ OTHER_SWIFT_FLAGS = "$(inherited) -Xfrontend -clang-header-expose-decls=has-expose-attr-or-stdlib";
216
+ PRODUCT_NAME = "$(TARGET_NAME)";
217
+ STRING_CATALOG_GENERATE_SYMBOLS = YES;
218
+ SWIFT_APPROACHABLE_CONCURRENCY = YES;
219
+ SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
220
+ SWIFT_EMIT_LOC_STRINGS = YES;
221
+ SWIFT_OBJC_INTEROP_MODE = objcxx;
222
+ SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
223
+ SWIFT_VERSION = 6.0;
224
+ TARGETED_DEVICE_FAMILY = "1,2";
225
+ };
226
+ name = Release;
227
+ };
228
+ B85AA9FE305B1D52002C8DF6 /* Debug configuration for PBXProject "ReactNitro" */ = {
229
+ isa = XCBuildConfiguration;
230
+ baseConfigurationReference = AABBCCDD0000000000000001;
231
+ buildSettings = {
232
+ ALWAYS_SEARCH_USER_PATHS = NO;
233
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
234
+ CLANG_ANALYZER_NONNULL = YES;
235
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
236
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
237
+ CLANG_ENABLE_MODULES = YES;
238
+ CLANG_ENABLE_OBJC_ARC = YES;
239
+ CLANG_ENABLE_OBJC_WEAK = YES;
240
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
241
+ CLANG_WARN_BOOL_CONVERSION = YES;
242
+ CLANG_WARN_COMMA = YES;
243
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
244
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
245
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
246
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
247
+ CLANG_WARN_EMPTY_BODY = YES;
248
+ CLANG_WARN_ENUM_CONVERSION = YES;
249
+ CLANG_WARN_INFINITE_RECURSION = YES;
250
+ CLANG_WARN_INT_CONVERSION = YES;
251
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
252
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
253
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
254
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
255
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
256
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
257
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
258
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
259
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
260
+ CLANG_WARN_UNREACHABLE_CODE = YES;
261
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
262
+ COPY_PHASE_STRIP = NO;
263
+ DEBUG_INFORMATION_FORMAT = dwarf;
264
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
265
+ ENABLE_TESTABILITY = YES;
266
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
267
+ GCC_C_LANGUAGE_STANDARD = gnu17;
268
+ GCC_DYNAMIC_NO_PIC = NO;
269
+ GCC_NO_COMMON_BLOCKS = YES;
270
+ GCC_OPTIMIZATION_LEVEL = 0;
271
+ GCC_PREPROCESSOR_DEFINITIONS = (
272
+ "DEBUG=1",
273
+ "$(inherited)",
274
+ );
275
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
276
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
277
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
278
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
279
+ GCC_WARN_UNUSED_FUNCTION = YES;
280
+ GCC_WARN_UNUSED_VARIABLE = YES;
281
+ IPHONEOS_DEPLOYMENT_TARGET = 16.0;
282
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
283
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
284
+ MTL_FAST_MATH = YES;
285
+ ONLY_ACTIVE_ARCH = YES;
286
+ SDKROOT = iphoneos;
287
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
288
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
289
+ };
290
+ name = Debug;
291
+ };
292
+ B85AA9FF305B1D52002C8DF6 /* Release configuration for PBXProject "ReactNitro" */ = {
293
+ isa = XCBuildConfiguration;
294
+ baseConfigurationReference = AABBCCDD0000000000000001;
295
+ buildSettings = {
296
+ ALWAYS_SEARCH_USER_PATHS = NO;
297
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
298
+ CLANG_ANALYZER_NONNULL = YES;
299
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
300
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
301
+ CLANG_ENABLE_MODULES = YES;
302
+ CLANG_ENABLE_OBJC_ARC = YES;
303
+ CLANG_ENABLE_OBJC_WEAK = YES;
304
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
305
+ CLANG_WARN_BOOL_CONVERSION = YES;
306
+ CLANG_WARN_COMMA = YES;
307
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
308
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
309
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
310
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
311
+ CLANG_WARN_EMPTY_BODY = YES;
312
+ CLANG_WARN_ENUM_CONVERSION = YES;
313
+ CLANG_WARN_INFINITE_RECURSION = YES;
314
+ CLANG_WARN_INT_CONVERSION = YES;
315
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
316
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
317
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
318
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
319
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
320
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
321
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
322
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
323
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
324
+ CLANG_WARN_UNREACHABLE_CODE = YES;
325
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
326
+ COPY_PHASE_STRIP = NO;
327
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
328
+ ENABLE_NS_ASSERTIONS = NO;
329
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
330
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
331
+ GCC_C_LANGUAGE_STANDARD = gnu17;
332
+ GCC_NO_COMMON_BLOCKS = YES;
333
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
334
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
335
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
336
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
337
+ GCC_WARN_UNUSED_FUNCTION = YES;
338
+ GCC_WARN_UNUSED_VARIABLE = YES;
339
+ IPHONEOS_DEPLOYMENT_TARGET = 16.0;
340
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
341
+ MTL_ENABLE_DEBUG_INFO = NO;
342
+ MTL_FAST_MATH = YES;
343
+ SDKROOT = iphoneos;
344
+ SWIFT_COMPILATION_MODE = wholemodule;
345
+ VALIDATE_PRODUCT = YES;
346
+ };
347
+ name = Release;
348
+ };
349
+ /* End XCBuildConfiguration section */
350
+
351
+ /* Begin XCConfigurationList section */
352
+ B85AA9E3305B1D50002C8DF6 /* Build configuration list for PBXProject "ReactNitro" */ = {
353
+ isa = XCConfigurationList;
354
+ buildConfigurations = (
355
+ B85AA9FE305B1D52002C8DF6 /* Debug configuration for PBXProject "ReactNitro" */,
356
+ B85AA9FF305B1D52002C8DF6 /* Release configuration for PBXProject "ReactNitro" */,
357
+ );
358
+ defaultConfigurationName = Release;
359
+ };
360
+ B85AA9FB305B1D52002C8DF6 /* Build configuration list for PBXNativeTarget "ReactNitro" */ = {
361
+ isa = XCConfigurationList;
362
+ buildConfigurations = (
363
+ B85AA9FC305B1D52002C8DF6 /* Debug configuration for PBXNativeTarget "ReactNitro" */,
364
+ B85AA9FD305B1D52002C8DF6 /* Release configuration for PBXNativeTarget "ReactNitro" */,
365
+ );
366
+ defaultConfigurationName = Release;
367
+ };
368
+ /* End XCConfigurationList section */
369
+
370
+ /* Begin XCLocalSwiftPackageReference section */
371
+ B8439CA4305C47AC00534242 /* XCLocalSwiftPackageReference "../native/react-nitro-renderer" */ = {
372
+ isa = XCLocalSwiftPackageReference;
373
+ relativePath = "../native/react-nitro-renderer";
374
+ };
375
+ B85AAA00305B23CA002C8DF6 /* XCLocalSwiftPackageReference "../native/react-nitro-runtime" */ = {
376
+ isa = XCLocalSwiftPackageReference;
377
+ relativePath = "../native/react-nitro-runtime";
378
+ };
379
+ B8655EB7305D6D7D00F54F82 /* XCLocalSwiftPackageReference "../native/react-nitro-networking" */ = {
380
+ isa = XCLocalSwiftPackageReference;
381
+ relativePath = "../native/react-nitro-networking";
382
+ };
383
+ /* End XCLocalSwiftPackageReference section */
384
+
385
+ /* Begin XCSwiftPackageProductDependency section */
386
+ B8439CA5305C47AC00534242 /* ReactNitroRenderer */ = {
387
+ isa = XCSwiftPackageProductDependency;
388
+ productName = ReactNitroRenderer;
389
+ };
390
+ B85AAA01305B23CA002C8DF6 /* ReactNitroRuntime */ = {
391
+ isa = XCSwiftPackageProductDependency;
392
+ productName = ReactNitroRuntime;
393
+ };
394
+ B8655EB8305D6D7D00F54F82 /* ReactNitroNetworking */ = {
395
+ isa = XCSwiftPackageProductDependency;
396
+ productName = ReactNitroNetworking;
397
+ };
398
+ /* End XCSwiftPackageProductDependency section */
399
+ };
400
+ rootObject = B85AA9E0305B1D50002C8DF6 /* Project object */;
401
+ validationLevel = 1;
402
+ }
@@ -0,0 +1,18 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Scheme LastUpgradeVersion="2700" version="1.3">
3
+ <BuildAction parallelizeBuildables="YES" buildImplicitDependencies="YES">
4
+ <BuildActionEntries>
5
+ <BuildActionEntry buildForTesting="YES" buildForRunning="YES" buildForProfiling="YES" buildForArchiving="YES" buildForAnalyzing="YES">
6
+ <BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="B85AA9E7305B1D50002C8DF6" BuildableName="ReactNitro.app" BlueprintName="ReactNitro" ReferencedContainer="container:ReactNitro.xcodeproj"/>
7
+ </BuildActionEntry>
8
+ </BuildActionEntries>
9
+ </BuildAction>
10
+ <LaunchAction buildConfiguration="Debug" selectedDebuggerIdentifier="Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier="Xcode.IDEFoundation.Launcher.LLDB" launchStyle="0" useCustomWorkingDirectory="NO" ignoresPersistentStateOnLaunch="NO" debugDocumentVersioning="YES" debugServiceExtension="internal" allowLocationSimulation="YES">
11
+ <BuildableProductRunnable runnableDebuggingMode="0">
12
+ <BuildableReference BuildableIdentifier="primary" BlueprintIdentifier="B85AA9E7305B1D50002C8DF6" BuildableName="ReactNitro.app" BlueprintName="ReactNitro" ReferencedContainer="container:ReactNitro.xcodeproj"/>
13
+ </BuildableProductRunnable>
14
+ </LaunchAction>
15
+ <ProfileAction buildConfiguration="Release" shouldUseLaunchSchemeArgsEnv="YES" savedToolIdentifier="" useCustomWorkingDirectory="NO" debugDocumentVersioning="YES"/>
16
+ <AnalyzeAction buildConfiguration="Debug"/>
17
+ <ArchiveAction buildConfiguration="Release" revealArchiveInOrganizer="YES"/>
18
+ </Scheme>
@@ -0,0 +1,78 @@
1
+ // swift-tools-version: 6.4
2
+
3
+ import PackageDescription
4
+
5
+ // `native` is prepared from the pinned nitro-core npm dependency by `bun install`.
6
+ let cxxSettings: [CXXSetting] = [
7
+ .define("DEBUG", .when(configuration: .debug)),
8
+ .define("NDEBUG", .when(configuration: .release)),
9
+ // This host uses Hermes' native UTF-16 string creation.
10
+ .define("NITRO_JSI_HAS_FAST_UTF16", to: "true"),
11
+ .disableWarning("nullability-extension"),
12
+ ]
13
+
14
+ let package = Package(
15
+ name: "NitroCore",
16
+ platforms: [.iOS(.v16), .macOS(.v13)],
17
+ products: [
18
+ .library(
19
+ name: "NitroModules",
20
+ targets: [
21
+ "NitroModulesCxx", "NitroModulesAppleCxx", "NitroModules", "NitroModulesSwiftBridge",
22
+ ])
23
+ ],
24
+ dependencies: [
25
+ .package(path: "../react-nitro-hermes"),
26
+ .package(
27
+ url: "https://github.com/margelo/simdutf-swift.git",
28
+ from: "1.0.1",
29
+ traits: ["UTF8", "UTF16", "ASCII"]
30
+ ),
31
+ ],
32
+ targets: [
33
+ .target(
34
+ name: "NitroModulesCxx",
35
+ dependencies: [.product(name: "HermesJSI", package: "react-nitro-hermes")],
36
+ path: "native/cpp",
37
+ sources: ["src"],
38
+ publicHeadersPath: "include",
39
+ cxxSettings: cxxSettings
40
+ ),
41
+ .target(
42
+ name: "NitroModulesAppleCxx",
43
+ dependencies: ["NitroModulesCxx"],
44
+ path: "native/apple",
45
+ sources: ["src"],
46
+ publicHeadersPath: "include",
47
+ cxxSettings: cxxSettings
48
+ ),
49
+ .target(
50
+ name: "NitroModules",
51
+ dependencies: [
52
+ "NitroModulesCxx", "NitroModulesAppleCxx",
53
+ .product(name: "SimdUTF", package: "simdutf-swift"),
54
+ ],
55
+ path: "native/apple",
56
+ sources: ["core", "utils"],
57
+ cxxSettings: cxxSettings,
58
+ swiftSettings: [
59
+ .swiftLanguageMode(.v6),
60
+ .enableUpcomingFeature("InternalImportsByDefault"),
61
+ .enableUpcomingFeature("MemberImportVisibility"),
62
+ .interoperabilityMode(.Cxx),
63
+ .unsafeFlags(["-Xfrontend", "-clang-header-expose-decls=has-expose-attr-or-stdlib"]),
64
+ ]
65
+ ),
66
+ .target(
67
+ name: "NitroModulesSwiftBridge",
68
+ dependencies: ["NitroModules", "NitroModulesAppleCxx"],
69
+ path: "native/apple/swift-bridge",
70
+ sources: ["src"],
71
+ publicHeadersPath: "include",
72
+ cxxSettings: cxxSettings + [
73
+ .unsafeFlags(["-I${GENERATED_MODULEMAP_DIR}", "-fno-modules"])
74
+ ]
75
+ ),
76
+ ],
77
+ cxxLanguageStandard: .cxx20
78
+ )
package/package.json CHANGED
@@ -1,6 +1,41 @@
1
1
  {
2
2
  "name": "react-nitro-cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.0.1-alpha.7",
4
+ "dependencies": {
5
+ "react-nitro-bundler": "0.0.1-alpha.7",
6
+ "react-nitro-core": "0.0.1-alpha.7",
7
+ "react-nitro-hermes": "0.0.1-alpha.7",
8
+ "react-nitro-navigation": "0.0.1-alpha.7",
9
+ "react-nitro-networking": "0.0.1-alpha.7",
10
+ "react-nitro-renderer": "0.0.1-alpha.7",
11
+ "react-nitro-runtime": "0.0.1-alpha.7",
12
+ "react-nitro-yoga": "0.0.1-alpha.7",
13
+ "nitro-core": "1.0.0-alpha.nightly.105.g69d365fc9b18",
14
+ "zod": "4.6.7"
15
+ },
16
+ "publishConfig": {
17
+ "tag": "latest",
18
+ "access": "public"
19
+ },
20
+ "description": "CLI and native build integration for React Nitro applications.",
21
+ "type": "module",
22
+ "license": "MIT",
23
+ "bin": {
24
+ "react-nitro": "./src/cli.ts"
25
+ },
26
+ "exports": {
27
+ "./tsconfig.json": "./tsconfig.json",
28
+ "./package.json": "./package.json"
29
+ },
30
+ "files": [
31
+ "src",
32
+ "ios",
33
+ "native",
34
+ "tsconfig.json"
35
+ ],
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/margelo/react-nitro.git",
39
+ "directory": "packages/react-nitro-cli"
40
+ }
41
+ }
@@ -0,0 +1,20 @@
1
+ import { join } from 'node:path'
2
+ import { z } from 'zod'
3
+
4
+ export const appConfigSchema = z.object({
5
+ name: z.string().regex(/^[A-Za-z][A-Za-z0-9]*$/),
6
+ displayName: z.string().min(1).optional(),
7
+ bundleIdentifier: z.string().regex(/^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/),
8
+ entry: z.string().min(1).default('src/index.tsx'),
9
+ version: z.string().regex(/^\d+\.\d+\.\d+$/).default('1.0.0'),
10
+ buildNumber: z.string().regex(/^\d+$/).default('1'),
11
+ developmentTeam: z.string().regex(/^[A-Z0-9]{10}$/).optional(),
12
+ })
13
+
14
+ export type AppConfig = z.infer<typeof appConfigSchema>
15
+
16
+ export async function readAppConfig(root: string): Promise<AppConfig> {
17
+ const path = join(root, 'app.json')
18
+ const contents: unknown = await Bun.file(path).json()
19
+ return appConfigSchema.parse(contents)
20
+ }
@@ -0,0 +1,30 @@
1
+ import { join } from 'node:path'
2
+ import { readAppConfig } from './appConfig'
3
+ import { prepare } from './prepare'
4
+ import { run } from './run'
5
+
6
+ export async function buildIOS(options: {
7
+ root: string
8
+ release: boolean
9
+ device: string | undefined
10
+ buildOnly: boolean
11
+ }): Promise<void> {
12
+ const { root, release, device, buildOnly } = options
13
+ await prepare(root, false)
14
+ const project = join(root, '.nitro/ios/ReactNitro.xcodeproj')
15
+ const derivedData = join(root, '.nitro/build')
16
+ const configuration = release ? 'Release' : 'Debug'
17
+ let destination = 'generic/platform=iOS Simulator'
18
+ if (device !== undefined) destination = `platform=iOS Simulator,id=${device}`
19
+ if (buildOnly === false && device === undefined) throw new Error('Specify --device <simulator UDID>, or use --build-only.')
20
+ await run([
21
+ 'xcodebuild', '-project', project, '-scheme', 'ReactNitro',
22
+ '-configuration', configuration, '-destination', destination,
23
+ '-derivedDataPath', derivedData, 'CODE_SIGNING_ALLOWED=NO', 'build',
24
+ ], root)
25
+ if (buildOnly || device === undefined) return
26
+ const app = join(derivedData, `Build/Products/${configuration}-iphonesimulator/ReactNitro.app`)
27
+ await run(['xcrun', 'simctl', 'install', device, app], root)
28
+ const config = await readAppConfig(root)
29
+ await run(['xcrun', 'simctl', 'launch', device, config.bundleIdentifier], root)
30
+ }
@@ -0,0 +1,39 @@
1
+ import { join, resolve } from 'node:path'
2
+ import { bundle } from 'react-nitro-bundler'
3
+ import { bundleRelease } from 'react-nitro-bundler/release'
4
+ import { readAppConfig } from './appConfig'
5
+
6
+ export async function bundleApp(options: {
7
+ root: string
8
+ release: boolean
9
+ output: string | undefined
10
+ entry: string | undefined
11
+ xcode: boolean
12
+ }): Promise<void> {
13
+ const { root, release, output, entry, xcode } = options
14
+ let entryPath: string
15
+ if (entry === undefined) {
16
+ const config = await readAppConfig(root)
17
+ entryPath = resolve(root, config.entry)
18
+ } else {
19
+ entryPath = resolve(root, entry)
20
+ }
21
+ let destination: string
22
+ let production = release
23
+ if (xcode) {
24
+ const directory = process.env.TARGET_BUILD_DIR
25
+ const resources = process.env.UNLOCALIZED_RESOURCES_FOLDER_PATH
26
+ if (directory === undefined || resources === undefined) throw new Error('The --xcode option requires an Xcode build environment.')
27
+ production = process.env.CONFIGURATION !== 'Debug'
28
+ const filename = production ? 'main.hbc' : 'main.js'
29
+ destination = join(directory, resources, filename)
30
+ } else {
31
+ if (output === undefined) throw new Error('Specify --output or --xcode.')
32
+ destination = resolve(root, output)
33
+ }
34
+ if (production) {
35
+ await bundleRelease(entryPath, destination)
36
+ } else {
37
+ await bundle(entryPath, destination)
38
+ }
39
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env bun
2
+ import { resolve } from 'node:path'
3
+ import { parseArgs } from 'node:util'
4
+ import { buildIOS } from './buildIOS'
5
+ import { bundleApp } from './bundleApp'
6
+ import { prepare } from './prepare'
7
+
8
+ try {
9
+ const { values, positionals } = parseArgs({
10
+ args: process.argv.slice(2),
11
+ allowPositionals: true,
12
+ options: {
13
+ root: { type: 'string' },
14
+ output: { type: 'string' },
15
+ entry: { type: 'string' },
16
+ device: { type: 'string' },
17
+ release: { type: 'boolean', default: false },
18
+ clean: { type: 'boolean', default: false },
19
+ xcode: { type: 'boolean', default: false },
20
+ 'build-only': { type: 'boolean', default: false },
21
+ help: { type: 'boolean', default: false },
22
+ },
23
+ })
24
+ const workingDirectory = process.cwd()
25
+ const root = resolve(values.root ?? workingDirectory)
26
+ const [command] = positionals
27
+ if (values.help || command === undefined) {
28
+ console.log('react-nitro prepare [--clean]\nreact-nitro ios --device <UDID> [--release]\nreact-nitro ios --build-only [--release]\nreact-nitro bundle --output <file> [--release]')
29
+ } else {
30
+ switch (command) {
31
+ case 'prepare':
32
+ await prepare(root, values.clean)
33
+ break
34
+ case 'ios':
35
+ await buildIOS({ root: root, release: values.release, device: values.device, buildOnly: values['build-only'] })
36
+ break
37
+ case 'bundle':
38
+ await bundleApp({ root: root, release: values.release, output: values.output, entry: values.entry, xcode: values.xcode })
39
+ break
40
+ default:
41
+ throw new Error(`Unknown command: ${command}`)
42
+ }
43
+ }
44
+ } catch (error) {
45
+ const message = error instanceof Error ? error.message : String(error)
46
+ console.error(message)
47
+ process.exitCode = 1
48
+ }
@@ -0,0 +1,9 @@
1
+ export const nativePackages = [
2
+ { name: 'react-nitro-hermes', files: ['Package.swift', 'Generated', 'Modules', 'hermes', 'Tests'] },
3
+ { name: 'react-nitro-yoga', files: ['Package.swift', 'Sources', 'Generated', 'include', 'yoga'] },
4
+ { name: 'react-nitro-core', files: ['Package.swift', 'Sources', 'nitrogen/generated'] },
5
+ { name: 'react-nitro-networking', files: ['Package.swift', 'Sources', 'nitrogen/generated'] },
6
+ { name: 'react-nitro-renderer', files: ['Package.swift', 'Sources', 'nitrogen/generated'] },
7
+ { name: 'react-nitro-navigation', files: ['Package.swift', 'Sources', 'nitrogen/generated'] },
8
+ { name: 'react-nitro-runtime', files: ['Package.swift', 'Sources'] },
9
+ ]
@@ -0,0 +1,9 @@
1
+ import { createRequire } from 'node:module'
2
+ import { dirname } from 'node:path'
3
+
4
+ const require = createRequire(import.meta.url)
5
+
6
+ export function packageDirectory(name: string): string {
7
+ const manifest = require.resolve(`${name}/package.json`)
8
+ return dirname(manifest)
9
+ }
package/src/prepare.ts ADDED
@@ -0,0 +1,78 @@
1
+ import { cp, mkdir, readFile, rm, writeFile } from 'node:fs/promises'
2
+ import { dirname, join, resolve } from 'node:path'
3
+ import { z } from 'zod'
4
+ import { readAppConfig } from './appConfig'
5
+ import { nativePackages } from './nativePackages'
6
+ import { packageDirectory } from './packageDirectory'
7
+ import { prepareNitroCore } from './prepareNitroCore'
8
+
9
+ const versionSchema = z.object({ version: z.string() })
10
+ const frameworkDirectory = resolve(import.meta.dir, '..')
11
+
12
+ export async function prepare(root: string, clean: boolean): Promise<void> {
13
+ const config = await readAppConfig(root)
14
+ const directory = join(root, '.nitro')
15
+ const nativeDirectory = join(directory, 'native')
16
+ const versions: Record<string, string> = {}
17
+ const packageNames = ['react-nitro-cli', 'nitro-core']
18
+ for (const item of nativePackages) packageNames.push(item.name)
19
+ for (const name of packageNames) {
20
+ const source = packageDirectory(name)
21
+ const path = join(source, 'package.json')
22
+ const contents: unknown = await Bun.file(path).json()
23
+ const manifest = versionSchema.parse(contents)
24
+ versions[name] = manifest.version
25
+ }
26
+ const marker = join(nativeDirectory, 'versions.json')
27
+ const serializedVersions = JSON.stringify(versions)
28
+ const markerExists = await Bun.file(marker).exists()
29
+ let prepared = false
30
+ if (markerExists && clean === false) {
31
+ const existing = await readFile(marker, 'utf8')
32
+ prepared = existing === serializedVersions
33
+ }
34
+ if (prepared === false) {
35
+ await rm(nativeDirectory, { recursive: true, force: true })
36
+ await mkdir(nativeDirectory, { recursive: true })
37
+ for (const item of nativePackages) {
38
+ const source = packageDirectory(item.name)
39
+ for (const file of item.files) {
40
+ const sourcePath = join(source, file)
41
+ const destination = join(nativeDirectory, item.name, file)
42
+ const parent = dirname(destination)
43
+ await mkdir(parent, { recursive: true })
44
+ await cp(sourcePath, destination, { recursive: true, dereference: true })
45
+ }
46
+ }
47
+ const nitroCore = join(nativeDirectory, 'nitro-core')
48
+ const manifest = join(frameworkDirectory, 'native/nitro-core/Package.swift')
49
+ await prepareNitroCore(nitroCore)
50
+ const destination = join(nitroCore, 'Package.swift')
51
+ await cp(manifest, destination)
52
+ await writeFile(marker, serializedVersions)
53
+ }
54
+
55
+ const iosDirectory = join(directory, 'ios')
56
+ const iosSource = join(frameworkDirectory, 'ios')
57
+ await rm(iosDirectory, { recursive: true, force: true })
58
+ await cp(iosSource, iosDirectory, { recursive: true, force: true })
59
+ const cli = join(frameworkDirectory, 'src/cli.ts')
60
+ const displayName = config.displayName ?? config.name
61
+ const escapedName = displayName.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;')
62
+ const plistPath = join(iosDirectory, 'ReactNitro/Info.plist')
63
+ const plist = await readFile(plistPath, 'utf8')
64
+ const configuredPlist = plist.replace('__DISPLAY_NAME__', escapedName)
65
+ await writeFile(plistPath, configuredPlist)
66
+ const settings = [
67
+ `PRODUCT_BUNDLE_IDENTIFIER = ${config.bundleIdentifier}`,
68
+ `MARKETING_VERSION = ${config.version}`,
69
+ `CURRENT_PROJECT_VERSION = ${config.buildNumber}`,
70
+ `DEVELOPMENT_TEAM = ${config.developmentTeam ?? ''}`,
71
+ `NITRO_PROJECT_ROOT = ${root}`,
72
+ `NITRO_CLI_PATH = ${cli}`,
73
+ ]
74
+ const configuration = settings.join('\n')
75
+ const configurationPath = join(iosDirectory, 'App.xcconfig')
76
+ await writeFile(configurationPath, configuration)
77
+ console.log(`Prepared ${config.name} in ${directory}`)
78
+ }
@@ -0,0 +1,12 @@
1
+ import { cp, mkdir, rm } from 'node:fs/promises'
2
+ import { join } from 'node:path'
3
+ import { packageDirectory } from './packageDirectory'
4
+
5
+ export async function prepareNitroCore(directory: string): Promise<void> {
6
+ const source = packageDirectory('nitro-core')
7
+ const nativeSource = join(source, 'native')
8
+ const destination = join(directory, 'native')
9
+ await mkdir(directory, { recursive: true })
10
+ await rm(destination, { recursive: true, force: true })
11
+ await cp(nativeSource, destination, { recursive: true, dereference: true })
12
+ }
package/src/run.ts ADDED
@@ -0,0 +1,5 @@
1
+ export async function run(command: string[], cwd: string): Promise<void> {
2
+ const child = Bun.spawn(command, { cwd: cwd, stdin: 'inherit', stdout: 'inherit', stderr: 'inherit' })
3
+ const status = await child.exited
4
+ if (status !== 0) throw new Error(`${command[0]} exited with status ${status}`)
5
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "jsx": "react-jsx",
7
+ "lib": ["ESNext"],
8
+ "types": [],
9
+ "strict": true,
10
+ "noEmit": true,
11
+ "skipLibCheck": true,
12
+ "esModuleInterop": true,
13
+ "resolveJsonModule": true,
14
+ "verbatimModuleSyntax": true
15
+ }
16
+ }