basalt-core 0.0.0-rc5

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.
Files changed (325) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cli/basalt.d.ts +3 -0
  3. package/dist/cli/basalt.d.ts.map +1 -0
  4. package/dist/cli/basalt.js +47 -0
  5. package/dist/cli/basalt.js.map +1 -0
  6. package/dist/cli/bundle-cli.d.ts +12 -0
  7. package/dist/cli/bundle-cli.d.ts.map +1 -0
  8. package/dist/cli/bundle-cli.js +124 -0
  9. package/dist/cli/bundle-cli.js.map +1 -0
  10. package/dist/cli/bundleWithAssets.d.ts +79 -0
  11. package/dist/cli/bundleWithAssets.d.ts.map +1 -0
  12. package/dist/cli/bundleWithAssets.js +198 -0
  13. package/dist/cli/bundleWithAssets.js.map +1 -0
  14. package/dist/cli/desktop.d.ts +339 -0
  15. package/dist/cli/desktop.d.ts.map +1 -0
  16. package/dist/cli/desktop.js +1029 -0
  17. package/dist/cli/desktop.js.map +1 -0
  18. package/dist/cli/doctor.d.ts +31 -0
  19. package/dist/cli/doctor.d.ts.map +1 -0
  20. package/dist/cli/doctor.js +283 -0
  21. package/dist/cli/doctor.js.map +1 -0
  22. package/dist/cli/init.d.ts +77 -0
  23. package/dist/cli/init.d.ts.map +1 -0
  24. package/dist/cli/init.js +442 -0
  25. package/dist/cli/init.js.map +1 -0
  26. package/dist/cli/metro.d.ts +49 -0
  27. package/dist/cli/metro.d.ts.map +1 -0
  28. package/dist/cli/metro.js +148 -0
  29. package/dist/cli/metro.js.map +1 -0
  30. package/dist/cli/packageApp.d.ts +102 -0
  31. package/dist/cli/packageApp.d.ts.map +1 -0
  32. package/dist/cli/packageApp.js +319 -0
  33. package/dist/cli/packageApp.js.map +1 -0
  34. package/dist/cli/steps.d.ts +34 -0
  35. package/dist/cli/steps.d.ts.map +1 -0
  36. package/dist/cli/steps.js +53 -0
  37. package/dist/cli/steps.js.map +1 -0
  38. package/dist/metro-config.d.ts +168 -0
  39. package/dist/metro-config.d.ts.map +1 -0
  40. package/dist/metro-config.js +671 -0
  41. package/dist/metro-config.js.map +1 -0
  42. package/dist/react-native.config.d.ts +7 -0
  43. package/dist/react-native.config.d.ts.map +1 -0
  44. package/dist/react-native.config.js +80 -0
  45. package/dist/react-native.config.js.map +1 -0
  46. package/dist/src/DragSource.d.ts +52 -0
  47. package/dist/src/DragSource.d.ts.map +1 -0
  48. package/dist/src/DragSource.js +97 -0
  49. package/dist/src/DragSource.js.map +1 -0
  50. package/dist/src/DropTarget.d.ts +58 -0
  51. package/dist/src/DropTarget.d.ts.map +1 -0
  52. package/dist/src/DropTarget.js +131 -0
  53. package/dist/src/DropTarget.js.map +1 -0
  54. package/dist/src/KeyHandler.d.ts +57 -0
  55. package/dist/src/KeyHandler.d.ts.map +1 -0
  56. package/dist/src/KeyHandler.js +48 -0
  57. package/dist/src/KeyHandler.js.map +1 -0
  58. package/dist/src/Menu.d.ts +72 -0
  59. package/dist/src/Menu.d.ts.map +1 -0
  60. package/dist/src/Menu.js +179 -0
  61. package/dist/src/Menu.js.map +1 -0
  62. package/dist/src/TitleBar.d.ts +75 -0
  63. package/dist/src/TitleBar.d.ts.map +1 -0
  64. package/dist/src/TitleBar.js +236 -0
  65. package/dist/src/TitleBar.js.map +1 -0
  66. package/dist/src/Window.d.ts +87 -0
  67. package/dist/src/Window.d.ts.map +1 -0
  68. package/dist/src/Window.js +248 -0
  69. package/dist/src/Window.js.map +1 -0
  70. package/dist/src/closeRequest.d.ts +55 -0
  71. package/dist/src/closeRequest.d.ts.map +1 -0
  72. package/dist/src/closeRequest.js +134 -0
  73. package/dist/src/closeRequest.js.map +1 -0
  74. package/dist/src/index.d.ts +45 -0
  75. package/dist/src/index.d.ts.map +1 -0
  76. package/dist/src/index.js +92 -0
  77. package/dist/src/index.js.map +1 -0
  78. package/dist/src/overrides/Alert.d.ts +69 -0
  79. package/dist/src/overrides/Alert.d.ts.map +1 -0
  80. package/dist/src/overrides/Alert.js +112 -0
  81. package/dist/src/overrides/Alert.js.map +1 -0
  82. package/dist/src/overrides/BaseViewConfig.d.ts +28 -0
  83. package/dist/src/overrides/BaseViewConfig.d.ts.map +1 -0
  84. package/dist/src/overrides/BaseViewConfig.js +72 -0
  85. package/dist/src/overrides/BaseViewConfig.js.map +1 -0
  86. package/dist/src/overrides/Platform.linux.d.ts +10 -0
  87. package/dist/src/overrides/Platform.linux.d.ts.map +1 -0
  88. package/dist/src/overrides/Platform.linux.js +15 -0
  89. package/dist/src/overrides/Platform.linux.js.map +1 -0
  90. package/dist/src/overrides/Platform.macos.d.ts +10 -0
  91. package/dist/src/overrides/Platform.macos.d.ts.map +1 -0
  92. package/dist/src/overrides/Platform.macos.js +15 -0
  93. package/dist/src/overrides/Platform.macos.js.map +1 -0
  94. package/dist/src/overrides/Platform.windows.d.ts +10 -0
  95. package/dist/src/overrides/Platform.windows.d.ts.map +1 -0
  96. package/dist/src/overrides/Platform.windows.js +15 -0
  97. package/dist/src/overrides/Platform.windows.js.map +1 -0
  98. package/dist/src/overrides/ReactDevToolsSettingsManager.d.ts +24 -0
  99. package/dist/src/overrides/ReactDevToolsSettingsManager.d.ts.map +1 -0
  100. package/dist/src/overrides/ReactDevToolsSettingsManager.js +30 -0
  101. package/dist/src/overrides/ReactDevToolsSettingsManager.js.map +1 -0
  102. package/dist/src/overrides/RefreshControl.d.ts +52 -0
  103. package/dist/src/overrides/RefreshControl.d.ts.map +1 -0
  104. package/dist/src/overrides/RefreshControl.js +92 -0
  105. package/dist/src/overrides/RefreshControl.js.map +1 -0
  106. package/dist/src/overrides/ScrollView.d.ts +39 -0
  107. package/dist/src/overrides/ScrollView.d.ts.map +1 -0
  108. package/dist/src/overrides/ScrollView.js +59 -0
  109. package/dist/src/overrides/ScrollView.js.map +1 -0
  110. package/dist/src/overrides/Share.d.ts +96 -0
  111. package/dist/src/overrides/Share.d.ts.map +1 -0
  112. package/dist/src/overrides/Share.js +97 -0
  113. package/dist/src/overrides/Share.js.map +1 -0
  114. package/dist/src/overrides/TextInput.d.ts +81 -0
  115. package/dist/src/overrides/TextInput.d.ts.map +1 -0
  116. package/dist/src/overrides/TextInput.js +213 -0
  117. package/dist/src/overrides/TextInput.js.map +1 -0
  118. package/dist/src/overrides/View.d.ts +36 -0
  119. package/dist/src/overrides/View.d.ts.map +1 -0
  120. package/dist/src/overrides/View.js +99 -0
  121. package/dist/src/overrides/View.js.map +1 -0
  122. package/dist/src/overrides/createPlatform.d.ts +32 -0
  123. package/dist/src/overrides/createPlatform.d.ts.map +1 -0
  124. package/dist/src/overrides/createPlatform.js +84 -0
  125. package/dist/src/overrides/createPlatform.js.map +1 -0
  126. package/dist/src/overrides/setUpDeveloperTools.d.ts +28 -0
  127. package/dist/src/overrides/setUpDeveloperTools.d.ts.map +1 -0
  128. package/dist/src/overrides/setUpDeveloperTools.js +84 -0
  129. package/dist/src/overrides/setUpDeveloperTools.js.map +1 -0
  130. package/dist/src/overrides/setUpXHR.d.ts +46 -0
  131. package/dist/src/overrides/setUpXHR.d.ts.map +1 -0
  132. package/dist/src/overrides/setUpXHR.js +236 -0
  133. package/dist/src/overrides/setUpXHR.js.map +1 -0
  134. package/dist/src/quitRequest.d.ts +55 -0
  135. package/dist/src/quitRequest.d.ts.map +1 -0
  136. package/dist/src/quitRequest.js +121 -0
  137. package/dist/src/quitRequest.js.map +1 -0
  138. package/dist/src/resourcePath.d.ts +18 -0
  139. package/dist/src/resourcePath.d.ts.map +1 -0
  140. package/dist/src/resourcePath.js +31 -0
  141. package/dist/src/resourcePath.js.map +1 -0
  142. package/dist/src/responseBody.d.ts +76 -0
  143. package/dist/src/responseBody.d.ts.map +1 -0
  144. package/dist/src/responseBody.js +94 -0
  145. package/dist/src/responseBody.js.map +1 -0
  146. package/dist/src/responseHeaders.d.ts +40 -0
  147. package/dist/src/responseHeaders.d.ts.map +1 -0
  148. package/dist/src/responseHeaders.js +75 -0
  149. package/dist/src/responseHeaders.js.map +1 -0
  150. package/dist/src/titleBarState.d.ts +43 -0
  151. package/dist/src/titleBarState.d.ts.map +1 -0
  152. package/dist/src/titleBarState.js +77 -0
  153. package/dist/src/titleBarState.js.map +1 -0
  154. package/dist/src/useContextMenu.d.ts +113 -0
  155. package/dist/src/useContextMenu.d.ts.map +1 -0
  156. package/dist/src/useContextMenu.js +109 -0
  157. package/dist/src/useContextMenu.js.map +1 -0
  158. package/dist/src/useDialog.d.ts +98 -0
  159. package/dist/src/useDialog.d.ts.map +1 -0
  160. package/dist/src/useDialog.js +112 -0
  161. package/dist/src/useDialog.js.map +1 -0
  162. package/dist/src/useDisplays.d.ts +70 -0
  163. package/dist/src/useDisplays.d.ts.map +1 -0
  164. package/dist/src/useDisplays.js +118 -0
  165. package/dist/src/useDisplays.js.map +1 -0
  166. package/dist/src/useHandledKeys.d.ts +38 -0
  167. package/dist/src/useHandledKeys.d.ts.map +1 -0
  168. package/dist/src/useHandledKeys.js +70 -0
  169. package/dist/src/useHandledKeys.js.map +1 -0
  170. package/dist/src/useWindow.d.ts +140 -0
  171. package/dist/src/useWindow.d.ts.map +1 -0
  172. package/dist/src/useWindow.js +233 -0
  173. package/dist/src/useWindow.js.map +1 -0
  174. package/dist/tsconfig.tsbuildinfo +1 -0
  175. package/native/CMakeLists.txt +355 -0
  176. package/native/bootstrap.sh +613 -0
  177. package/native/cmake/CompilerFlags.cmake +49 -0
  178. package/native/cmake/Expo.cmake +125 -0
  179. package/native/cmake/ReactNativeCore.cmake +612 -0
  180. package/native/cmake/Reanimated.cmake +108 -0
  181. package/native/cmake/Skia.cmake +379 -0
  182. package/native/cmake/ThirdParty.cmake +290 -0
  183. package/native/cmake/Worklets.cmake +67 -0
  184. package/native/cmake/codegen/CMakeLists.txt +32 -0
  185. package/native/core/AppIdentity.cpp +85 -0
  186. package/native/core/AppIdentity.h +59 -0
  187. package/native/core/AppPaths.cpp +96 -0
  188. package/native/core/AppPaths.h +53 -0
  189. package/native/core/AppearanceModule.cpp +60 -0
  190. package/native/core/AppearanceModule.h +40 -0
  191. package/native/core/Backface.h +59 -0
  192. package/native/core/BlobModule.cpp +283 -0
  193. package/native/core/BlobModule.h +76 -0
  194. package/native/core/BlobRegistry.cpp +75 -0
  195. package/native/core/BlobRegistry.h +44 -0
  196. package/native/core/ColorScheme.cpp +105 -0
  197. package/native/core/ColorScheme.h +59 -0
  198. package/native/core/ComponentRegistry.h +30 -0
  199. package/native/core/ControlMetrics.h +37 -0
  200. package/native/core/CoreModules.cpp +592 -0
  201. package/native/core/CoreModules.h +308 -0
  202. package/native/core/DebuggingOverlay.cpp +106 -0
  203. package/native/core/DebuggingOverlay.h +65 -0
  204. package/native/core/DesktopControls.cpp +232 -0
  205. package/native/core/DesktopControls.h +170 -0
  206. package/native/core/DevBundle.cpp +108 -0
  207. package/native/core/DevBundle.h +69 -0
  208. package/native/core/DevMenu.cpp +99 -0
  209. package/native/core/DevMenu.h +75 -0
  210. package/native/core/DialogModule.cpp +150 -0
  211. package/native/core/DialogModule.h +51 -0
  212. package/native/core/DragAndDrop.cpp +45 -0
  213. package/native/core/DragAndDrop.h +225 -0
  214. package/native/core/ExpoImageComponent.cpp +79 -0
  215. package/native/core/ExpoImageComponent.h +80 -0
  216. package/native/core/ExpoModules.cpp +415 -0
  217. package/native/core/ExpoModules.h +115 -0
  218. package/native/core/ExpoRuntime.cpp +366 -0
  219. package/native/core/ExpoRuntime.h +42 -0
  220. package/native/core/FocusRing.h +36 -0
  221. package/native/core/FontRegistry.h +51 -0
  222. package/native/core/GestureHandlerModule.cpp +136 -0
  223. package/native/core/GestureHandlerModule.h +71 -0
  224. package/native/core/Gestures.cpp +533 -0
  225. package/native/core/Gestures.h +145 -0
  226. package/native/core/HoverTracker.cpp +69 -0
  227. package/native/core/HoverTracker.h +109 -0
  228. package/native/core/HttpClient.cpp +282 -0
  229. package/native/core/ImageBytes.cpp +205 -0
  230. package/native/core/ImageBytes.h +29 -0
  231. package/native/core/ImageCache.cpp +51 -0
  232. package/native/core/ImageCache.h +89 -0
  233. package/native/core/JsRuntimeAccess.cpp +70 -0
  234. package/native/core/JsRuntimeAccess.h +60 -0
  235. package/native/core/JsiPromise.h +51 -0
  236. package/native/core/KeyEvents.cpp +116 -0
  237. package/native/core/KeyEvents.h +144 -0
  238. package/native/core/LogBoxSurface.cpp +46 -0
  239. package/native/core/LogBoxSurface.h +68 -0
  240. package/native/core/MenuModel.h +117 -0
  241. package/native/core/MenuModule.cpp +209 -0
  242. package/native/core/MenuModule.h +66 -0
  243. package/native/core/MountingWalk.h +524 -0
  244. package/native/core/PackageModules.cpp.in +25 -0
  245. package/native/core/PackageModules.h +50 -0
  246. package/native/core/PlatformConstantsModule.cpp +63 -0
  247. package/native/core/PlatformConstantsModule.h +64 -0
  248. package/native/core/PlatformServices.h +213 -0
  249. package/native/core/PointerButtons.h +63 -0
  250. package/native/core/PullToRefresh.cpp +57 -0
  251. package/native/core/PullToRefresh.h +73 -0
  252. package/native/core/ReanimatedCompat.h +47 -0
  253. package/native/core/ReanimatedModule.cpp +203 -0
  254. package/native/core/ReanimatedModule.h +55 -0
  255. package/native/core/ScrollAnimation.cpp +59 -0
  256. package/native/core/ScrollAnimation.h +70 -0
  257. package/native/core/ScrollBounds.cpp +26 -0
  258. package/native/core/ScrollBounds.h +68 -0
  259. package/native/core/ScrollIndicator.cpp +50 -0
  260. package/native/core/ScrollIndicator.h +69 -0
  261. package/native/core/ScrollMomentum.cpp +71 -0
  262. package/native/core/ScrollMomentum.h +73 -0
  263. package/native/core/ScrollSnap.cpp +123 -0
  264. package/native/core/ScrollSnap.h +80 -0
  265. package/native/core/ShareFallback.cpp +101 -0
  266. package/native/core/ShareFallback.h +45 -0
  267. package/native/core/SkiaPictureViewComponent.cpp +11 -0
  268. package/native/core/SkiaPictureViewComponent.h +47 -0
  269. package/native/core/SourceCodeModule.cpp +55 -0
  270. package/native/core/SourceCodeModule.h +53 -0
  271. package/native/core/StatusBarModule.cpp +23 -0
  272. package/native/core/StatusBarModule.h +42 -0
  273. package/native/core/TestDialog.cpp +148 -0
  274. package/native/core/TestDialog.h +96 -0
  275. package/native/core/TestKeys.cpp +78 -0
  276. package/native/core/TestKeys.h +50 -0
  277. package/native/core/TestQuitFile.cpp +33 -0
  278. package/native/core/TestQuitFile.h +60 -0
  279. package/native/core/TestSettle.h +192 -0
  280. package/native/core/TitleBarRegions.h +31 -0
  281. package/native/core/TurboModuleProxy.cpp +51 -0
  282. package/native/core/TurboModuleProxy.h +46 -0
  283. package/native/core/UIManagerAccess.cpp +43 -0
  284. package/native/core/UIManagerAccess.h +43 -0
  285. package/native/core/WindowBoundsCache.cpp +319 -0
  286. package/native/core/WindowControl.h +235 -0
  287. package/native/core/WindowHost.h +158 -0
  288. package/native/core/WindowMethods.cpp +167 -0
  289. package/native/core/WindowMethods.h +84 -0
  290. package/native/core/WindowsModule.cpp +448 -0
  291. package/native/core/WindowsModule.h +133 -0
  292. package/native/core/WorkletsModule.cpp +199 -0
  293. package/native/core/WorkletsModule.h +57 -0
  294. package/native/core/portability_probe.cpp +210 -0
  295. package/native/tests/EventRecorder.h +124 -0
  296. package/native/tests/TestHarness.cpp +109 -0
  297. package/native/tests/TestHarness.h +89 -0
  298. package/native/tests/TreeDump.h +49 -0
  299. package/native/tests/test_app_identity.cpp +114 -0
  300. package/native/tests/test_app_paths.cpp +48 -0
  301. package/native/tests/test_appearance.cpp +107 -0
  302. package/native/tests/test_backface.cpp +79 -0
  303. package/native/tests/test_blobs.cpp +157 -0
  304. package/native/tests/test_controls.cpp +185 -0
  305. package/native/tests/test_devbundle.cpp +103 -0
  306. package/native/tests/test_drag_and_drop.cpp +143 -0
  307. package/native/tests/test_gestures.cpp +331 -0
  308. package/native/tests/test_hover.cpp +92 -0
  309. package/native/tests/test_image_cache.cpp +109 -0
  310. package/native/tests/test_js_runtime_access.cpp +137 -0
  311. package/native/tests/test_key_events.cpp +235 -0
  312. package/native/tests/test_momentum.cpp +138 -0
  313. package/native/tests/test_quit_file.cpp +104 -0
  314. package/native/tests/test_scroll_animation.cpp +120 -0
  315. package/native/tests/test_scroll_bounds.cpp +66 -0
  316. package/native/tests/test_scroll_indicator.cpp +149 -0
  317. package/native/tests/test_scroll_snap.cpp +112 -0
  318. package/native/tests/test_settle.cpp +115 -0
  319. package/native/tests/test_share.cpp +75 -0
  320. package/native/tests/test_sourcecode.cpp +62 -0
  321. package/native/tests/test_window_close.cpp +119 -0
  322. package/native/tests/test_window_limits.cpp +123 -0
  323. package/package.json +43 -0
  324. package/react-native.config.js +86 -0
  325. package/supported-versions.json +66 -0
@@ -0,0 +1,1029 @@
1
+ "use strict";
2
+ /**
3
+ * `react-native run-linux`, `run-macos` and `run-windows`, which are one
4
+ * command with three names.
5
+ *
6
+ * Everything a desktop run does is the same on all three: find or build a host
7
+ * binary, work out the module name, make sure a packager is running, write a
8
+ * bundle, launch the host in the foreground and hand back its exit status. What
9
+ * differs is four strings -- the platform, the command name, the binary, and
10
+ * where its native sources are -- so that is what a platform package passes in.
11
+ *
12
+ * The same division as the C++ half, for the same reason. Three copies of this
13
+ * file would be three places for `--mode release` to mean something slightly
14
+ * different, and the first anyone would hear of it is an app that bundles
15
+ * without assets on one desktop.
16
+ *
17
+ * @format
18
+ */
19
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ var desc = Object.getOwnPropertyDescriptor(m, k);
22
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
23
+ desc = { enumerable: true, get: function() { return m[k]; } };
24
+ }
25
+ Object.defineProperty(o, k2, desc);
26
+ }) : (function(o, m, k, k2) {
27
+ if (k2 === undefined) k2 = k;
28
+ o[k2] = m[k];
29
+ }));
30
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
31
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
32
+ }) : function(o, v) {
33
+ o["default"] = v;
34
+ });
35
+ var __importStar = (this && this.__importStar) || (function () {
36
+ var ownKeys = function(o) {
37
+ ownKeys = Object.getOwnPropertyNames || function (o) {
38
+ var ar = [];
39
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
40
+ return ar;
41
+ };
42
+ return ownKeys(o);
43
+ };
44
+ return function (mod) {
45
+ if (mod && mod.__esModule) return mod;
46
+ var result = {};
47
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
48
+ __setModuleDefault(result, mod);
49
+ return result;
50
+ };
51
+ })();
52
+ Object.defineProperty(exports, "__esModule", { value: true });
53
+ exports.MissingHost = void 0;
54
+ exports.isExecutable = isExecutable;
55
+ exports.candidates = candidates;
56
+ exports.resolveHost = resolveHost;
57
+ exports.reactNativeSources = reactNativeSources;
58
+ exports.capabilityPackages = capabilityPackages;
59
+ exports.localNativeModules = localNativeModules;
60
+ exports.optionalNativeModules = optionalNativeModules;
61
+ exports.compilerArgs = compilerArgs;
62
+ exports.findGitBash = findGitBash;
63
+ exports.parseSetOutput = parseSetOutput;
64
+ exports.msvcEnvironment = msvcEnvironment;
65
+ exports.monorepoRoot = monorepoRoot;
66
+ exports.explainContributedFailure = explainContributedFailure;
67
+ exports.migrateBuildDirectory = migrateBuildDirectory;
68
+ exports.buildHost = buildHost;
69
+ exports.resolveModuleName = resolveModuleName;
70
+ exports.missingMetroConfig = missingMetroConfig;
71
+ exports.makeRunCommand = makeRunCommand;
72
+ const node_child_process_1 = require("node:child_process");
73
+ const fs = __importStar(require("node:fs"));
74
+ const path = __importStar(require("node:path"));
75
+ const bundleWithAssets_1 = require("./bundleWithAssets");
76
+ const packageApp_1 = require("./packageApp");
77
+ const metro_1 = require("./metro");
78
+ const DEFAULT_PORT = 8081;
79
+ class MissingHost extends Error {
80
+ }
81
+ exports.MissingHost = MissingHost;
82
+ /**
83
+ * Executable, on a platform that has an opinion about it.
84
+ *
85
+ * `X_OK` is meaningless on Windows -- every readable file answers yes -- so the
86
+ * question there is only whether it is a file. Asking the same question of a
87
+ * Unix binary would call a non-executable one runnable, which is worse than
88
+ * either mistake this avoids.
89
+ */
90
+ function isExecutable(file) {
91
+ try {
92
+ if (!fs.statSync(file).isFile()) {
93
+ return false;
94
+ }
95
+ if (process.platform !== 'win32') {
96
+ fs.accessSync(file, fs.constants.X_OK);
97
+ }
98
+ return true;
99
+ }
100
+ catch {
101
+ return false;
102
+ }
103
+ }
104
+ /**
105
+ * Where to look for the host, in order of how explicit the answer is.
106
+ *
107
+ * The last entry is the development checkout: when the platform package is
108
+ * being used from inside its own repository, the build tree is four
109
+ * directories up. That is what makes the demo app in examples/ work without
110
+ * configuration.
111
+ */
112
+ function candidates(projectRoot, options, target) {
113
+ const found = [];
114
+ if (options.hostBinary) {
115
+ found.push(path.resolve(projectRoot, options.hostBinary));
116
+ }
117
+ if (process.env.BASALT_HOST) {
118
+ found.push(path.resolve(process.env.BASALT_HOST));
119
+ }
120
+ // Where `--build` puts one, per target; see buildHost.
121
+ found.push(path.join(projectRoot, '.basalt', 'build', target.platform, target.binary));
122
+ // And where it used to, before the directory was split by target. Kept so
123
+ // that a checkout built before that change still runs without rebuilding.
124
+ found.push(path.join(projectRoot, '.basalt', 'build', target.binary));
125
+ found.push(path.join(projectRoot, target.platform, 'build', target.binary));
126
+ found.push(path.join(projectRoot, 'build', target.binary));
127
+ found.push(path.join(target.nativeDir, '..', '..', '..', 'build', target.binary));
128
+ return found.map(entry => path.normalize(entry));
129
+ }
130
+ function resolveHost(projectRoot, options, target) {
131
+ const looked = candidates(projectRoot, options, target);
132
+ const found = looked.find(isExecutable);
133
+ if (found) {
134
+ return found;
135
+ }
136
+ throw new MissingHost(`could not find ${target.binary}.\n\n` +
137
+ 'Looked in:\n' +
138
+ looked.map(entry => ` ${entry}`).join('\n') +
139
+ '\n\n' +
140
+ 'Build one with:\n\n' +
141
+ ` react-native ${target.command} --build\n\n` +
142
+ `That needs cmake, ninja, and ${target.toolchain}. ` +
143
+ 'Or build it\nyourself and point at it with --host-binary <path> or BASALT_HOST.');
144
+ }
145
+ function run(command, args, options, toolchain) {
146
+ const result = (0, node_child_process_1.spawnSync)(command, args, { stdio: 'inherit', ...options });
147
+ if (result.error) {
148
+ if (result.error.code === 'ENOENT') {
149
+ throw new Error(`${command} is not installed. Building the host needs cmake, ninja, and ${toolchain}.`);
150
+ }
151
+ throw result.error;
152
+ }
153
+ if (result.status !== 0) {
154
+ throw new Error(`${command} ${args[0] ?? ''} failed`);
155
+ }
156
+ }
157
+ /**
158
+ * The React Native to build against, in either of the two shapes bootstrap
159
+ * accepts.
160
+ *
161
+ * A checkout, where bootstrap is handed the monorepo root and CMake the
162
+ * packages/react-native beneath it; or an installed package, where both are
163
+ * handed the package itself. The second is what every app has. React Native's
164
+ * npm package ships everything the host compiles against except
165
+ * ReactCxxPlatform, and bootstrap fetches that at the app's exact version --
166
+ * see docs/PORTING.md.
167
+ *
168
+ * This used to refuse an installed package outright, saying the npm package
169
+ * lacked the C++, long after bootstrap and CMake had learned to build from one.
170
+ * The command was the only part that had not.
171
+ */
172
+ function reactNativeSources(reactNativePath) {
173
+ const root = monorepoRoot(reactNativePath);
174
+ if (root != null) {
175
+ return {
176
+ layout: 'checkout',
177
+ bootstrapArg: root,
178
+ rnDir: path.join(root, 'packages', 'react-native'),
179
+ };
180
+ }
181
+ let real = reactNativePath;
182
+ try {
183
+ real = fs.realpathSync(reactNativePath);
184
+ }
185
+ catch {
186
+ // Keep the original; the check below reports it.
187
+ }
188
+ if (!fs.existsSync(path.join(real, 'ReactCommon'))) {
189
+ throw new Error(`react-native resolves to ${reactNativePath}, which has no ReactCommon: it is ` +
190
+ 'neither a React Native checkout nor an installed react-native package.');
191
+ }
192
+ return { layout: 'installed', bootstrapArg: real, rnDir: real };
193
+ }
194
+ /**
195
+ * A package's directory, found the way Node finds one: node_modules/<name> in
196
+ * `from` or in any directory above it.
197
+ *
198
+ * Walked by hand rather than through require.resolve, because resolving
199
+ * `<name>/package.json` throws for any package whose `exports` does not list
200
+ * it, and the only question here is where the package is on disk.
201
+ */
202
+ function findPackage(name, from) {
203
+ let dir = path.resolve(from);
204
+ for (;;) {
205
+ const candidate = path.join(dir, 'node_modules', name);
206
+ if (fs.existsSync(path.join(candidate, 'package.json'))) {
207
+ return candidate;
208
+ }
209
+ const parent = path.dirname(dir);
210
+ if (parent === dir) {
211
+ return null;
212
+ }
213
+ dir = parent;
214
+ }
215
+ }
216
+ /**
217
+ * The optional native halves an app brings with it, as CMake definitions.
218
+ *
219
+ * Expo's runtime, worklets and Reanimated each compile from the app's own copy
220
+ * of the package (cmake/Expo.cmake and its siblings), and each is off unless
221
+ * named. `--build` never named them, so the host it built for an Expo app had
222
+ * no Expo runtime and the app failed at its first Expo import. Now whatever the
223
+ * app has installed is what gets built.
224
+ *
225
+ * expo-modules-core is looked for beside `expo` as well, since it is `expo`'s
226
+ * dependency rather than the app's, and a package manager that does not hoist
227
+ * leaves it there. Reanimated without worklets is skipped with a note rather
228
+ * than failing the configure: Reanimated 4 is built on worklets and
229
+ * Reanimated.cmake requires both.
230
+ */
231
+ /**
232
+ * Capability packages: anything installed that declares native code of its own.
233
+ *
234
+ * A package says so in its own manifest -- `"basalt": {"native": "..."}` -- and
235
+ * the build reads that rather than this file knowing its name. Which is the
236
+ * difference between this and the three special cases below: Expo, worklets and
237
+ * Reanimated are third-party packages that will never carry the key, so they
238
+ * stay hardcoded; anything shipped for this platform does not have to be.
239
+ *
240
+ * Scanned from the app's own dependencies rather than the whole of
241
+ * node_modules: a transitive dependency contributing C++ to the host binary
242
+ * without the app asking is not a thing to make easy.
243
+ */
244
+ function capabilityPackages(projectRoot) {
245
+ const manifestPath = path.join(projectRoot, 'package.json');
246
+ if (!fs.existsSync(manifestPath)) {
247
+ return [];
248
+ }
249
+ let manifest;
250
+ try {
251
+ manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
252
+ }
253
+ catch {
254
+ return [];
255
+ }
256
+ const names = [
257
+ ...Object.keys(manifest.dependencies ?? {}),
258
+ ...Object.keys(manifest.devDependencies ?? {}),
259
+ ];
260
+ const found = [];
261
+ for (const name of names) {
262
+ const dir = findPackage(name, projectRoot);
263
+ if (dir == null) {
264
+ continue;
265
+ }
266
+ let declared;
267
+ try {
268
+ declared = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8'));
269
+ }
270
+ catch {
271
+ continue;
272
+ }
273
+ const native = declared?.basalt?.native;
274
+ if (typeof native !== 'string') {
275
+ continue;
276
+ }
277
+ if (!fs.existsSync(path.join(dir, native))) {
278
+ throw new Error(`${name} declares "basalt.native": ${JSON.stringify(native)} and that file ` +
279
+ `does not exist. A package that says it has native code and does not is a ` +
280
+ `host built without it, which fails later and further away.`);
281
+ }
282
+ found.push(dir);
283
+ }
284
+ return found;
285
+ }
286
+ /**
287
+ * The app's own native modules: `<projectRoot>/modules/*` with a
288
+ * `native/CMakeLists.txt` in it.
289
+ *
290
+ * Expo's autolinking looks in exactly this directory for an app's local modules
291
+ * -- `expo-module.config.json` there is how `requireNativeModule('KinoProcess')`
292
+ * finds native code that was never published -- and an app that has written an
293
+ * iOS half and an Android half of one has nowhere to put the desktop half. A
294
+ * package would be the alternative, and publishing a package to yourself, so
295
+ * that one binary can compile a file already sitting in the repo, is not a
296
+ * reasonable thing to ask.
297
+ *
298
+ * **Presence is the declaration**, unlike a capability package, which says so in
299
+ * its manifest. A local module has no manifest to say it in: `create-expo-module
300
+ * --local` writes a config, an index and a platform directory, and no
301
+ * package.json. So the desktop half is declared the way the Apple half is --
302
+ * by being there -- and `native/CMakeLists.txt` is the name, because that is
303
+ * already what a host's package loop looks for.
304
+ *
305
+ * The rule capabilityPackages exists to keep is kept here for free: this is the
306
+ * app's own source tree, so nothing gets compiled into the binary that the app
307
+ * did not write.
308
+ */
309
+ function localNativeModules(projectRoot) {
310
+ const modulesDir = path.join(projectRoot, 'modules');
311
+ let entries;
312
+ try {
313
+ entries = fs.readdirSync(modulesDir, { withFileTypes: true });
314
+ }
315
+ catch {
316
+ // No modules directory, which is every app that has not made one.
317
+ return [];
318
+ }
319
+ const found = [];
320
+ for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
321
+ if (!entry.isDirectory() && !entry.isSymbolicLink()) {
322
+ continue;
323
+ }
324
+ const dir = path.join(modulesDir, entry.name);
325
+ if (fs.existsSync(path.join(dir, 'native', 'CMakeLists.txt'))) {
326
+ found.push(dir);
327
+ }
328
+ }
329
+ return found;
330
+ }
331
+ function optionalNativeModules(projectRoot, platform) {
332
+ const args = [];
333
+ const notes = [];
334
+ // Forward slashes: CMake reads a backslash in a -D value as an escape.
335
+ const define = (name, dir) => args.push(`-D${name}=${dir.split(path.sep).join('/')}`);
336
+ const expo = findPackage('expo', projectRoot);
337
+ const expoCore = findPackage('expo-modules-core', projectRoot) ??
338
+ (expo != null ? findPackage('expo-modules-core', expo) : null);
339
+ if (expoCore != null) {
340
+ define('BASALT_EXPO_MODULES_CORE', expoCore);
341
+ notes.push(`Expo's runtime, from ${expoCore}`);
342
+ }
343
+ const worklets = findPackage('react-native-worklets', projectRoot);
344
+ if (worklets != null) {
345
+ define('BASALT_WORKLETS', worklets);
346
+ notes.push(`worklets, from ${worklets}`);
347
+ }
348
+ // @shopify/react-native-skia, if the archives for this target are there.
349
+ //
350
+ // A question about the *archives*, not the platform. macOS has published
351
+ // ones; Linux and Windows have whatever scripts/build_skia_linux.sh and its
352
+ // Windows counterpart put in the package, laid out the way the package lays
353
+ // out Android's. Asking the filesystem is what lets a host that built them
354
+ // use them, and still says the useful thing to one that has not.
355
+ //
356
+ // It replaced a platform gate, which is worth remembering for the shape of
357
+ // the bug rather than the bug. That gate first asked `process.platform`,
358
+ // which is the machine doing the building and not the thing being built, so
359
+ // a GTK or Win32 build started from a Mac was handed Apple's archives and
360
+ // said nothing. Fixing it to ask about the target was right and was still
361
+ // only right until somebody built Skia for the other two, at which point
362
+ // "which platform is this" had become the wrong question entirely and
363
+ // "what is on disk" was the one underneath it all along.
364
+ //
365
+ // The note matters as much as the define. A missing native module is not
366
+ // missing quietly here: the package reaches for
367
+ // TurboModuleRegistry.getEnforcing in its own JavaScript, so an app that
368
+ // merely imports it does not start, and that exception carries nothing about
369
+ // the build decision behind it. This is the only place it can be explained.
370
+ const skia = findPackage('@shopify/react-native-skia', projectRoot);
371
+ const skiaArchive = {
372
+ macos: path.join('libs', 'macos', 'libskia.xcframework'),
373
+ windows: path.join('libs', 'windows', 'x86_64', 'skia.lib'),
374
+ // Per architecture, because an ELF archive is. The build machine's
375
+ // architecture is the right one to ask about here and nowhere else in
376
+ // this function: a Linux host is built natively, so the machine doing
377
+ // the building is the machine that will run it.
378
+ linux: path.join('libs', 'linux', process.arch === 'arm64' ? 'aarch64' : 'x86_64', 'libskia.a'),
379
+ };
380
+ const archive = skia != null ? path.join(skia, skiaArchive[platform] ?? '') : null;
381
+ if (skia != null && archive != null && fs.existsSync(archive)) {
382
+ define('BASALT_SKIA', skia);
383
+ notes.push(`Skia, from ${skia}`);
384
+ }
385
+ else if (skia != null) {
386
+ const how = platform === 'macos'
387
+ ? 'Run the app\'s pod install, or `yarn install-skia` inside the package.'
388
+ : `Nobody publishes any for ${platform}: the package ships Apple and ` +
389
+ 'Android archives and nothing else. scripts/build_skia_linux.sh builds ' +
390
+ 'them, and puts them where this looked.';
391
+ notes.push(`not building @shopify/react-native-skia (${skia}): no archives at ` +
392
+ `${archive}. ${how} This host will have no RNSkiaModule, and importing ` +
393
+ 'the package at all -- not rendering with it, importing it -- ends the ' +
394
+ "app at getEnforcing('RNSkiaModule'). Import it behind a check if this " +
395
+ 'target matters.');
396
+ }
397
+ const reanimated = findPackage('react-native-reanimated', projectRoot);
398
+ if (reanimated != null && worklets != null) {
399
+ define('BASALT_REANIMATED', reanimated);
400
+ notes.push(`Reanimated, from ${reanimated}`);
401
+ }
402
+ else if (reanimated != null) {
403
+ notes.push(`not building react-native-reanimated (${reanimated}): it needs ` +
404
+ 'react-native-worklets, which is not installed');
405
+ }
406
+ // And anything that declares native code: a dependency that says so in its own
407
+ // manifest, and a module the app wrote under `modules/`. One -D holding a list,
408
+ // because CMake reads a semicolon-separated value as a list and the root
409
+ // CMakeLists iterates it.
410
+ // And anything the app wrote itself under `modules/`, which is where Expo's
411
+ // autolinking looks for an app's own native modules.
412
+ const packages = [...capabilityPackages(projectRoot), ...localNativeModules(projectRoot)];
413
+ if (packages.length > 0) {
414
+ const dirs = packages.map(dir => dir.split(path.sep).join('/'));
415
+ args.push(`-DBASALT_PACKAGES=${dirs.join(';')}`);
416
+ for (const dir of packages) {
417
+ notes.push(`capability package, from ${dir}`);
418
+ }
419
+ }
420
+ return { args, notes };
421
+ }
422
+ /**
423
+ * What the configure step has to name rather than leave to CMake's defaults.
424
+ *
425
+ * **The compiler.** React Native's C++ is built -Wall -Werror -Wpedantic and is
426
+ * warning-clean only under clang, which is why bootstrap's own configure line,
427
+ * CI and the README all name it. Left alone, CMake picks the system default --
428
+ * g++ on Ubuntu, cl on Windows -- and `--build` left it alone: a fresh Expo
429
+ * app's first build on Ubuntu 24.04 compiled Hermes (bootstrap names clang)
430
+ * and then stopped in ReactCommon on GCC's class-memaccess and pedantic
431
+ * __int128 errors. CC and CXX still win, as they would for a plain cmake.
432
+ *
433
+ * **On Windows, two more.** CMAKE_BUILD_TYPE, because unset MSVC picks the debug
434
+ * runtime and vcpkg follows it, while Hermes was built Release, and lld-link's
435
+ * complaint about _ITERATOR_DEBUG_LEVEL names neither. And vcpkg's toolchain
436
+ * file, found the way bootstrap.sh finds vcpkg: by a header it cannot do
437
+ * without, not by VCPKG_ROOT, which vcvars64.bat points at an empty copy.
438
+ */
439
+ function compilerArgs(platform, env = process.env) {
440
+ const args = [];
441
+ const named = Boolean(env.CC || env.CXX);
442
+ if (platform !== 'windows') {
443
+ if (!named) {
444
+ args.push('-DCMAKE_C_COMPILER=clang', '-DCMAKE_CXX_COMPILER=clang++');
445
+ }
446
+ return args;
447
+ }
448
+ if (!named) {
449
+ args.push('-DCMAKE_C_COMPILER=clang-cl', '-DCMAKE_CXX_COMPILER=clang-cl');
450
+ }
451
+ // And the linker by name, which naming the compiler does not settle.
452
+ //
453
+ // CMake picks a linker by searching PATH, and a machine with MSYS2, MinGW or
454
+ // Git's own toolchain installed has a GNU `ld` on it -- which it will
455
+ // happily choose for a clang-cl build and then fail on the first MSVC flag
456
+ // it is handed:
457
+ //
458
+ // ld.exe: cannot find /nologo: No such file or directory
459
+ //
460
+ // A confusing error, because nothing is wrong with the project and the
461
+ // compiler it names is correct. lld-link ships beside clang-cl in the same
462
+ // Visual Studio component, so if one was found the other is there too.
463
+ if (!env.LD) {
464
+ args.push('-DCMAKE_LINKER=lld-link');
465
+ }
466
+ args.push('-DCMAKE_BUILD_TYPE=RelWithDebInfo');
467
+ const home = env.USERPROFILE || env.HOME || '';
468
+ const roots = [
469
+ env.VCPKG_ROOT,
470
+ home && path.join(home, 'Tools', 'vcpkg'),
471
+ home && path.join(home, 'vcpkg'),
472
+ 'C:/vcpkg',
473
+ ].filter((root) => typeof root === 'string' && root.length > 0);
474
+ for (const root of roots) {
475
+ const toolchain = path.join(root, 'scripts', 'buildsystems', 'vcpkg.cmake');
476
+ const glog = path.join(root, 'installed', 'x64-windows', 'include', 'glog', 'logging.h');
477
+ if (fs.existsSync(toolchain) && fs.existsSync(glog)) {
478
+ args.push(`-DCMAKE_TOOLCHAIN_FILE=${toolchain.split(path.sep).join('/')}`);
479
+ break;
480
+ }
481
+ }
482
+ return args;
483
+ }
484
+ /**
485
+ * Git Bash, found rather than assumed.
486
+ *
487
+ * bootstrap.sh runs under Git Bash on Windows, and `bash` by name is not it on
488
+ * any machine with WSL: System32\bash.exe and WindowsApps\bash.exe both come
489
+ * first on PATH, and both launch Linux. Run there, bootstrap sees a Linux host
490
+ * and a Windows checkout and fails in ways that name neither.
491
+ *
492
+ * So: BASALT_BASH if set; else the bash.exe beside whichever git.exe is on PATH
493
+ * (Git for Windows puts git.exe in <Git>\cmd and bash.exe in <Git>\bin); else
494
+ * the standard install locations. Anything under System32 or WindowsApps is
495
+ * refused wherever it came from.
496
+ */
497
+ function findGitBash(env = process.env) {
498
+ // Either separator: a path handed in through BASALT_BASH or PATH may use
499
+ // forward slashes, and the tests run this on Linux too.
500
+ const isLauncher = (candidate) => /[\\/](system32|windowsapps)[\\/]/i.test(candidate);
501
+ const usable = (candidate) => candidate != null && !isLauncher(candidate) && fs.existsSync(candidate);
502
+ if (env.BASALT_BASH) {
503
+ return usable(env.BASALT_BASH) ? env.BASALT_BASH : null;
504
+ }
505
+ const found = [];
506
+ for (const dir of (env.PATH || env.Path || '').split(';').filter(Boolean)) {
507
+ if (fs.existsSync(path.join(dir, 'git.exe'))) {
508
+ // <Git>\cmd\git.exe, or <Git>\mingw64\bin\git.exe.
509
+ found.push(path.join(dir, '..', 'bin', 'bash.exe'));
510
+ found.push(path.join(dir, '..', '..', 'bin', 'bash.exe'));
511
+ }
512
+ }
513
+ for (const root of [env.ProgramW6432, env.ProgramFiles, env.LOCALAPPDATA && path.join(env.LOCALAPPDATA, 'Programs')]) {
514
+ if (root) {
515
+ found.push(path.join(root, 'Git', 'bin', 'bash.exe'));
516
+ }
517
+ }
518
+ return found.map(candidate => path.normalize(candidate)).find(usable) ?? null;
519
+ }
520
+ /**
521
+ * The environment `set` prints, as an object. Only lines of the form NAME=value;
522
+ * cmd prints nothing else there, but a banner from a profile script might.
523
+ */
524
+ function parseSetOutput(output) {
525
+ const env = {};
526
+ for (const line of output.split(/\r?\n/)) {
527
+ const match = /^([^=\s][^=]*)=(.*)$/.exec(line);
528
+ if (match) {
529
+ env[match[1]] = match[2];
530
+ }
531
+ }
532
+ return env;
533
+ }
534
+ /**
535
+ * The MSVC environment, loaded the way a Developer Command Prompt loads it.
536
+ *
537
+ * clang-cl needs the Windows SDK's headers and libraries, and only
538
+ * vcvars64.bat puts them on INCLUDE and LIB; the Visual Studio CMake component
539
+ * it also puts on PATH brings cmake and ninja. Without this, `--build` worked
540
+ * only from a shell somebody had already prepared, and preparing one from
541
+ * PowerShell meant a wrapper script -- which is how an example app came to
542
+ * forward its arguments into npm's npx.ps1 with @args, and lose every one of
543
+ * them.
544
+ *
545
+ * Skipped when the shell already has it (VCToolsInstallDir is vcvars' own
546
+ * marker). VCPKG_ROOT is left as it was: vcvars points it at the copy bundled
547
+ * with Visual Studio, which has nothing installed in it.
548
+ */
549
+ function msvcEnvironment(env = process.env) {
550
+ if (env.VCToolsInstallDir) {
551
+ return env;
552
+ }
553
+ const programFilesX86 = env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)';
554
+ const vswhere = path.join(programFilesX86, 'Microsoft Visual Studio', 'Installer', 'vswhere.exe');
555
+ if (!fs.existsSync(vswhere)) {
556
+ throw new Error('no Visual Studio found (vswhere.exe is missing). Building the host on Windows needs ' +
557
+ 'the Visual Studio Build Tools with the C++ workload and clang-cl.');
558
+ }
559
+ const where = (0, node_child_process_1.spawnSync)(vswhere, ['-latest', '-products', '*', '-requires', 'Microsoft.VisualStudio.Component.VC.Llvm.Clang', '-property', 'installationPath'], { encoding: 'utf8' });
560
+ const installation = (where.stdout || '').trim().split(/\r?\n/)[0];
561
+ if (!installation) {
562
+ throw new Error('no Visual Studio with clang-cl found. Add "C++ Clang Compiler for Windows" to the ' +
563
+ 'Build Tools installation.');
564
+ }
565
+ const vcvars = path.join(installation, 'VC', 'Auxiliary', 'Build', 'vcvars64.bat');
566
+ // Quoted twice over. With /s, cmd strips the first and last quote from what
567
+ // follows /c and runs the rest, so a single pair around the path -- which has
568
+ // spaces in it, under Program Files (x86) -- loses both of its quotes and
569
+ // runs `C:\Program`. The outer pair is the one cmd removes.
570
+ const loaded = (0, node_child_process_1.spawnSync)('cmd.exe', ['/d', '/s', '/c', `""${vcvars}" >nul 2>&1 && set"`], {
571
+ encoding: 'utf8',
572
+ env,
573
+ windowsVerbatimArguments: true,
574
+ });
575
+ if (loaded.status !== 0) {
576
+ throw new Error(`${vcvars} failed`);
577
+ }
578
+ const merged = { ...env, ...parseSetOutput(loaded.stdout || '') };
579
+ if (env.VCPKG_ROOT === undefined) {
580
+ delete merged.VCPKG_ROOT;
581
+ }
582
+ else {
583
+ merged.VCPKG_ROOT = env.VCPKG_ROOT;
584
+ }
585
+ return merged;
586
+ }
587
+ /**
588
+ * The React Native monorepo root, when `reactNativePath` is inside a checkout.
589
+ *
590
+ * `reactNativePath` is the package directory, two levels below the root in a
591
+ * checkout. An installed react-native has no monorepo above it, and this
592
+ * returns null for one; reactNativeSources above decides what to do then.
593
+ */
594
+ function monorepoRoot(reactNativePath) {
595
+ // Through the symlink first. A workspace, a pnpm store and `npm link` all
596
+ // put a link in node_modules, and walking up from the link lands in the app
597
+ // rather than in the checkout it points at.
598
+ let real = reactNativePath;
599
+ try {
600
+ real = fs.realpathSync(reactNativePath);
601
+ }
602
+ catch {
603
+ // Keep the original and let the check below decide.
604
+ }
605
+ const candidate = path.resolve(real, '..', '..');
606
+ if (fs.existsSync(path.join(candidate, 'packages', 'react-native'))) {
607
+ return candidate;
608
+ }
609
+ return null;
610
+ }
611
+ /**
612
+ * Building the host from an app.
613
+ *
614
+ * This is what `run-android` gets from Gradle and `run-ios` from Xcode. There
615
+ * is no equivalent to hand it to, so the work is here: vendor React Native's
616
+ * C++ dependencies, configure CMake against the app's own React Native, and
617
+ * build.
618
+ *
619
+ * Two things about where output goes. It goes under the app, in `.basalt`, not
620
+ * into `node_modules`, because npm rewrites node_modules on install and a
621
+ * Hermes build is not something to lose that way. And `third_party` is shared
622
+ * between builds of the same app rather than per configuration, because it
623
+ * depends on the React Native version and nothing else; the version stamp
624
+ * inside it is what keeps that honest.
625
+ *
626
+ * The first build is slow in a way `run-android` is not. It compiles Hermes and
627
+ * React Native's C++ core. Saying so before it starts is better than a silent
628
+ * half hour.
629
+ */
630
+ /**
631
+ * A build failure, with what was in the build.
632
+ *
633
+ * A capability package -- or one of the app's own modules -- compiles into the
634
+ * same binary as the platform, so its compiler errors arrive looking exactly
635
+ * like the platform's own: a path, in a node_modules directory nobody was
636
+ * thinking about. Naming what contributed is the part that cannot be read off
637
+ * the error.
638
+ *
639
+ * Which package broke it is deliberately not claimed. The build output is
640
+ * streamed rather than captured, because a person watching a twenty-minute
641
+ * compile should see it happen, and buffering it to grep for a directory would
642
+ * trade that for a guess. Naming the contributors is what can be said honestly
643
+ * without giving that up.
644
+ */
645
+ function explainContributedFailure(projectRoot, error) {
646
+ let contributors = [];
647
+ try {
648
+ contributors = [...capabilityPackages(projectRoot), ...localNativeModules(projectRoot)];
649
+ }
650
+ catch {
651
+ // The packages could not even be listed, which is its own error and not
652
+ // one to raise from inside the handler for a different one.
653
+ return error;
654
+ }
655
+ if (contributors.length === 0) {
656
+ return error;
657
+ }
658
+ const names = contributors.map(dir => path.basename(dir)).join(', ');
659
+ return new Error(`${error.message}\n\n` +
660
+ `This host was built with native code from ${contributors.length} ` +
661
+ `contributor${contributors.length === 1 ? '' : 's'}: ${names}. ` +
662
+ `A compiler error naming a path inside one of those is that contributor's ` +
663
+ `rather than the platform's.`);
664
+ }
665
+ /**
666
+ * Clears a build directory from before it was split by target.
667
+ *
668
+ * `.basalt/build` used to be the build directory itself; it is now a directory
669
+ * of them, one per target. The old one cannot become the new one: a CMake build
670
+ * directory records its own path, and moving it produces "The current
671
+ * CMakeCache.txt directory ... is different than the directory ..." -- which was
672
+ * this function's first shape, and it did not work.
673
+ *
674
+ * So it is moved aside rather than salvaged, and the target is built once more.
675
+ * That costs less than it sounds: the expensive vendoring -- Hermes, folly,
676
+ * ReactCxxPlatform -- lives in `.basalt/third_party`, which is untouched, and
677
+ * ccache carries most of the rest.
678
+ *
679
+ * Kept rather than deleted, because it is hundreds of megabytes of someone
680
+ * else's disk and the CLI is not the right thing to decide that. The line it
681
+ * prints says where it went.
682
+ */
683
+ function migrateBuildDirectory(workDir, log) {
684
+ const build = path.join(workDir, 'build');
685
+ if (!fs.existsSync(path.join(build, 'CMakeCache.txt'))) {
686
+ return;
687
+ }
688
+ const parked = path.join(workDir, 'build.before-split');
689
+ fs.rmSync(parked, { recursive: true, force: true });
690
+ fs.renameSync(build, parked);
691
+ fs.mkdirSync(build, { recursive: true });
692
+ log(`builds are per target now, and a CMake build directory cannot be moved. ` +
693
+ `The previous one is at ${parked} -- this target builds once more, then ` +
694
+ `incrementally. Delete it when you are happy.`);
695
+ }
696
+ function buildHost(context, options, target) {
697
+ const projectRoot = context.root;
698
+ const workDir = path.join(projectRoot, '.basalt');
699
+ // One per target, because a build directory belongs to the host it was
700
+ // configured for. CMake refuses to reuse one whose source directory has
701
+ // changed -- it says the two paths do not match and explains neither -- so a
702
+ // single `.basalt/build` meant that building for a second desktop from one
703
+ // checkout destroyed the first. Found by building a macOS app for Linux.
704
+ const buildDir = path.join(workDir, 'build', target.platform);
705
+ const thirdParty = path.join(workDir, 'third_party');
706
+ const sources = reactNativeSources(context.reactNativePath);
707
+ const nativeModules = optionalNativeModules(projectRoot, target.platform);
708
+ fs.mkdirSync(workDir, { recursive: true });
709
+ migrateBuildDirectory(workDir, line => console.log(` ${line}`));
710
+ console.log(`==> React Native from ${sources.layout === 'installed' ? 'the installed package' : 'a checkout'}: ${sources.rnDir}`);
711
+ for (const note of nativeModules.notes) {
712
+ console.log(` ${note}`);
713
+ }
714
+ console.log("==> vendoring React Native's C++ dependencies");
715
+ console.log(' The first run compiles Hermes and takes a while.');
716
+ const bootstrap = path.join(target.coreDir, 'bootstrap.sh');
717
+ // Through bash by name, everywhere. On Windows a .sh file is not executable
718
+ // and there is no shebang handling, and the shell that runs it is Git Bash,
719
+ // which is what bootstrap.sh detects and expects. And on Linux and macOS the
720
+ // executable bit is only there if whoever packed the package had one to
721
+ // give: a tarball packed on Windows has none, and running the script directly
722
+ // would fail with EACCES for exactly the people installing from npm.
723
+ //
724
+ // On Windows it has to be Git Bash specifically, and everything from here on
725
+ // needs the MSVC environment; see findGitBash and msvcEnvironment.
726
+ let buildEnv = process.env;
727
+ let bash = 'bash';
728
+ if (process.platform === 'win32') {
729
+ buildEnv = msvcEnvironment(process.env);
730
+ const found = findGitBash(buildEnv);
731
+ if (found == null) {
732
+ throw new Error('building the host on Windows needs Git Bash, and none was found -- only a WSL ' +
733
+ 'launcher, or nothing. Install Git for Windows, or set BASALT_BASH to its bash.exe.');
734
+ }
735
+ }
736
+ run(bash, [bootstrap, sources.bootstrapArg], { cwd: workDir, env: { ...buildEnv, BASALT_THIRD_PARTY: thirdParty } }, target.toolchain);
737
+ console.log('==> configuring');
738
+ run('cmake', [
739
+ '-S',
740
+ target.nativeDir,
741
+ '-B',
742
+ buildDir,
743
+ '-G',
744
+ target.generator ?? 'Ninja',
745
+ `-DRN_DIR=${sources.rnDir}`,
746
+ ...nativeModules.args,
747
+ ...compilerArgs(target.platform),
748
+ `-DBASALT_THIRD_PARTY=${thirdParty}`,
749
+ // Where the shared half is. CMake has a default that covers a checkout
750
+ // and a hoisted node_modules, and naming it here covers pnpm's store too,
751
+ // where the two packages are not siblings on disk at all.
752
+ `-DBASALT_CORE_DIR=${target.coreDir}`,
753
+ ...(target.configureArgs ?? []),
754
+ ], { env: buildEnv }, target.toolchain);
755
+ console.log('==> building');
756
+ const jobs = options.jobs ? ['-j', String(options.jobs)] : [];
757
+ try {
758
+ run('cmake', ['--build', buildDir, ...jobs], { env: buildEnv }, target.toolchain);
759
+ }
760
+ catch (error) {
761
+ throw explainContributedFailure(projectRoot, error);
762
+ }
763
+ const binary = path.join(buildDir, target.binary);
764
+ if (!isExecutable(binary)) {
765
+ throw new Error(`the build produced no ${target.binary} at ${binary}`);
766
+ }
767
+ return binary;
768
+ }
769
+ /**
770
+ * The name the app registered with AppRegistry.
771
+ *
772
+ * app.json's `name`, which is what `registerRootComponent` and every template
773
+ * use, and what run-android reads for the same purpose. Expo apps register
774
+ * "main" regardless, so an explicit --module has to win.
775
+ */
776
+ function resolveModuleName(projectRoot, options) {
777
+ if (typeof options.module === 'string') {
778
+ return options.module;
779
+ }
780
+ const appJson = path.join(projectRoot, 'app.json');
781
+ if (fs.existsSync(appJson)) {
782
+ try {
783
+ const parsed = JSON.parse(fs.readFileSync(appJson, 'utf8'));
784
+ if (typeof parsed.name === 'string' && parsed.name.length > 0) {
785
+ return parsed.name;
786
+ }
787
+ }
788
+ catch (error) {
789
+ throw new Error(`could not read ${appJson}: ${error.message}`);
790
+ }
791
+ }
792
+ throw new Error('no module name: add a "name" to app.json, or pass --module <name>.\n' +
793
+ 'It is the name the app passes to AppRegistry.registerComponent.');
794
+ }
795
+ /**
796
+ * One bundler for every app.
797
+ *
798
+ * React Native's CLI could do this, and did, but only an app that has that CLI
799
+ * can reach it -- an Expo app cannot. Since the same code has to exist for
800
+ * them, it may as well be the only path, so both kinds of app get identical
801
+ * output and there is one place where assets can go wrong.
802
+ */
803
+ async function bundleForRelease(context, options, outputPath, target) {
804
+ const result = await (0, bundleWithAssets_1.bundleWithAssets)({
805
+ projectRoot: context.root,
806
+ entryFile: options.entryFile ?? 'index.js',
807
+ bundleOutput: outputPath,
808
+ platform: target.platform,
809
+ dev: false,
810
+ });
811
+ console.log(` ${result.files} asset file${result.files === 1 ? '' : 's'} beside the bundle`);
812
+ }
813
+ function launch(hostBinary, { bundlePath, moduleName, dev, port, entry, cwd }) {
814
+ const env = { ...process.env };
815
+ if (dev) {
816
+ env.BASALT_DEV = '1';
817
+ env.BASALT_DEV_PORT = String(port);
818
+ env.BASALT_DEV_ENTRY = entry;
819
+ }
820
+ else {
821
+ delete env.BASALT_DEV;
822
+ }
823
+ return new Promise((resolve, reject) => {
824
+ const child = (0, node_child_process_1.spawn)(hostBinary, [bundlePath ?? '', moduleName], {
825
+ cwd,
826
+ env,
827
+ stdio: 'inherit',
828
+ });
829
+ child.on('error', reject);
830
+ child.on('exit', (code, signal) => {
831
+ // A signal is how a windowed app normally ends here, and Ctrl-C reaches
832
+ // the child directly because it shares this terminal's process group.
833
+ resolve(signal != null ? 0 : (code ?? 0));
834
+ });
835
+ });
836
+ }
837
+ /**
838
+ * Whether React Native's `start` command can load its Metro configuration in
839
+ * this app. Null when it can; otherwise the message to show.
840
+ *
841
+ * Development runs start Metro through that command, and its loader requires
842
+ * @react-native/metro-config from the app whatever the app's own
843
+ * metro.config.js says. A React Native template lists it. An Expo app does
844
+ * not -- Expo's CLI brings its own configuration -- so every Expo app's first
845
+ * development run died inside Metro with "Cannot resolve
846
+ * `@react-native/metro-config`", and only after the port timeout. Asked up
847
+ * front, the answer can name the version to install: the app's react-native
848
+ * version, which is what @react-native/community-cli-plugin pins it to.
849
+ */
850
+ function missingMetroConfig(projectRoot) {
851
+ if (findPackage('@react-native/metro-config', projectRoot) != null) {
852
+ return null;
853
+ }
854
+ let spec = '@react-native/metro-config';
855
+ const reactNative = findPackage('react-native', projectRoot);
856
+ if (reactNative != null) {
857
+ try {
858
+ const { version } = JSON.parse(fs.readFileSync(path.join(reactNative, 'package.json'), 'utf8'));
859
+ if (version) {
860
+ spec = `@react-native/metro-config@${version}`;
861
+ }
862
+ }
863
+ catch {
864
+ // No version to name; the bare package name is still the right advice.
865
+ }
866
+ }
867
+ return ("a development run starts Metro through React Native's `start` command, which " +
868
+ 'needs @react-native/metro-config installed in the app, and this app has none. ' +
869
+ 'Expo apps do not ship it. Install it with:\n\n' +
870
+ ` npm install --save-dev ${spec}\n\n` +
871
+ 'or run a bundle, which needs no Metro, with --mode release.');
872
+ }
873
+ async function runDesktop(_argv, context, options, target) {
874
+ const projectRoot = context.root;
875
+ const port = Number(options.port) || DEFAULT_PORT;
876
+ const dev = options.mode !== 'release';
877
+ // --build is explicit rather than automatic. A first build compiles Hermes
878
+ // and React Native's C++ core, which is not something a command should start
879
+ // on its own because a binary happened to be missing.
880
+ let hostBinary;
881
+ if (options.build) {
882
+ hostBinary = buildHost(context, options, target);
883
+ }
884
+ else {
885
+ hostBinary = resolveHost(projectRoot, options, target);
886
+ }
887
+ const moduleName = resolveModuleName(projectRoot, options);
888
+ // Named relative to the project so that the message is readable, but passed
889
+ // to the host as an absolute path, since it runs with the project as its
890
+ // working directory and a relative bundle would resolve differently.
891
+ const bundlePath = path.resolve(projectRoot, options.bundle ?? path.join('build', 'main.jsbundle.js'));
892
+ // The binary becomes an application before it is run, on every run rather
893
+ // than only for a release.
894
+ //
895
+ // That is not thoroughness, it is the only way the two agree: on macOS
896
+ // `NSBundle.mainBundle` comes from where the executable sits, so a host run
897
+ // out of a build directory has no bundle identifier and cannot notify, while
898
+ // the same code inside a `.app` can. Packaging only for release would mean a
899
+ // feature that works in production and not in development, which is the worse
900
+ // half of a difference between them. See cli/packageApp.js.
901
+ const packaged = (0, packageApp_1.packageApp)({
902
+ platform: target.platform,
903
+ hostBinary,
904
+ outputDir: path.resolve(projectRoot, 'build'),
905
+ projectRoot,
906
+ bundlePath,
907
+ });
908
+ hostBinary = packaged.launchPath;
909
+ if (packaged.appPath != null) {
910
+ console.log(`==> ${path.relative(projectRoot, packaged.appPath)}`);
911
+ }
912
+ if (packaged.desktopPath != null) {
913
+ const relative = path.relative(projectRoot, packaged.desktopPath);
914
+ console.log(`==> wrote ${relative}\n` +
915
+ ` install it to give this app its own name and icon on notifications:\n` +
916
+ ` desktop-file-install --dir="$HOME/.local/share/applications" ${relative}`);
917
+ }
918
+ if (dev) {
919
+ if (options.packager === false) {
920
+ console.log(`==> not starting Metro; expecting one on port ${port}`);
921
+ }
922
+ else if (await (0, metro_1.isPortTaken)(port)) {
923
+ console.log(`==> reusing the packager already on port ${port}`);
924
+ }
925
+ else {
926
+ const problem = missingMetroConfig(projectRoot);
927
+ if (problem != null) {
928
+ throw new Error(problem);
929
+ }
930
+ const { logPath } = await (0, metro_1.startMetro)(context, port);
931
+ console.log(`==> started Metro on port ${port}, logging to ${path.relative(projectRoot, logPath)}`);
932
+ }
933
+ // Still needed in development: the host falls back to this file when Metro
934
+ // cannot be reached, and a missing one turns a packager problem into a
935
+ // confusing failure to load anything at all.
936
+ if (!fs.existsSync(bundlePath)) {
937
+ console.log('==> writing a fallback bundle for when Metro is unreachable');
938
+ fs.mkdirSync(path.dirname(bundlePath), { recursive: true });
939
+ await bundleForRelease(context, options, bundlePath, target);
940
+ }
941
+ // Every dev run, not only the one that wrote the fallback bundle: the host
942
+ // reads app.config.json from beside the bundle, and an app.json edited
943
+ // since would otherwise be invisible in development and applied in release
944
+ // -- the worse half of a difference between the two.
945
+ await (0, bundleWithAssets_1.writeExpoAppConfig)(projectRoot, bundlePath);
946
+ }
947
+ else {
948
+ console.log('==> bundling for release');
949
+ await bundleForRelease(context, options, bundlePath, target);
950
+ }
951
+ console.log(`==> ${path.basename(hostBinary)} ${moduleName}${dev ? ' (dev)' : ''}`);
952
+ const status = await launch(hostBinary, {
953
+ bundlePath,
954
+ moduleName,
955
+ dev,
956
+ port,
957
+ entry: (options.entryFile ?? 'index.js').replace(/\.[^.]+$/, ''),
958
+ cwd: projectRoot,
959
+ });
960
+ if (status !== 0) {
961
+ throw new Error(`the app exited with status ${status}`);
962
+ }
963
+ }
964
+ /**
965
+ * The CLI command object React Native's CLI expects, for one target.
966
+ */
967
+ function makeRunCommand(target) {
968
+ return {
969
+ name: target.command,
970
+ description: `builds nothing and runs your app on ${target.label}, against a packager`,
971
+ func: async (argv, context, options) => {
972
+ try {
973
+ await runDesktop(argv, context, options, target);
974
+ }
975
+ catch (error) {
976
+ if (error instanceof MissingHost) {
977
+ // Its message is the instructions; a stack trace on top would bury
978
+ // them.
979
+ console.error(`\nerror: ${error.message}\n`);
980
+ process.exitCode = 1;
981
+ return;
982
+ }
983
+ throw error;
984
+ }
985
+ },
986
+ options: [
987
+ {
988
+ name: '--port <number>',
989
+ description: 'the port Metro is or should be on',
990
+ default: DEFAULT_PORT,
991
+ },
992
+ {
993
+ name: '--mode <string>',
994
+ description: '"dev" to run against Metro, "release" to run a bundle',
995
+ default: 'dev',
996
+ },
997
+ {
998
+ name: '--no-packager',
999
+ description: 'do not start Metro, and do not check for one',
1000
+ },
1001
+ {
1002
+ name: '--build',
1003
+ description: 'build the host before running it, into .basalt/build',
1004
+ },
1005
+ {
1006
+ name: '--jobs <number>',
1007
+ description: 'parallelism for --build',
1008
+ },
1009
+ {
1010
+ name: '--host-binary <path>',
1011
+ description: `the ${target.binary} to run, if it is not somewhere obvious`,
1012
+ },
1013
+ {
1014
+ name: '--module <string>',
1015
+ description: "the AppRegistry name to start, if it is not app.json's",
1016
+ },
1017
+ {
1018
+ name: '--bundle <path>',
1019
+ description: 'where the release bundle is written and read',
1020
+ },
1021
+ {
1022
+ name: '--entry-file <path>',
1023
+ description: 'the app entry point',
1024
+ default: 'index.js',
1025
+ },
1026
+ ],
1027
+ };
1028
+ }
1029
+ //# sourceMappingURL=desktop.js.map