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,80 @@
1
+ // expo-image's view, as a Fabric component.
2
+ //
3
+ // An Expo module's *functions* are an object in `globalThis.expo.modules`; see
4
+ // core/ExpoModules.h. An Expo module's *views* are not. `requireNativeViewManager`
5
+ // asks `globalThis.expo.getViewConfig(moduleName)` for a static view config and
6
+ // registers an ordinary React Native component called
7
+ // `ViewManagerAdapter_<moduleName>` with it. From that point on it is a Fabric
8
+ // component like any other: a name, a props class, a shadow node, a descriptor
9
+ // in the registry, and a mounting peer on each platform.
10
+ //
11
+ // So this file is the second half of the Expo port, and the half that is not
12
+ // Expo-specific at all -- there is nothing here that expo-modules-core has to
13
+ // be present for, which is why it compiles unconditionally. What needs Expo is
14
+ // the view config, and that lives with the rest of the runtime.
15
+ //
16
+ // **The view config is the contract.** React Native filters props against
17
+ // `validAttributes` in JavaScript before they are ever diffed, so a prop absent
18
+ // from the config never reaches C++ -- silently. That is the same trap phase 26
19
+ // found with `shouldNotifyLoadEvents`, and here both sides are ours, so the
20
+ // list in ExpoRuntime and the parsing below have to be read together.
21
+ //
22
+ // What is supported is what this platform can draw: a source, a content fit, a
23
+ // tint. expo-image's placeholder, transition, blurhash, cache policy and SF
24
+ // Symbols are left out of the view config, so they are dropped in JavaScript
25
+ // and never travel. An app using them gets an image without them rather than an
26
+ // error, which is the honest outcome for a prop whose effect is decorative.
27
+
28
+ #pragma once
29
+
30
+ #include <react/renderer/components/view/ConcreteViewShadowNode.h>
31
+ #include <react/renderer/components/view/ViewEventEmitter.h>
32
+ #include <react/renderer/components/view/ViewProps.h>
33
+ #include <react/renderer/core/ConcreteComponentDescriptor.h>
34
+ #include <react/renderer/imagemanager/primitives.h>
35
+
36
+ namespace facebook::react {
37
+
38
+ extern const char ExpoImageComponentName[];
39
+
40
+ class ExpoImageProps final : public ViewProps {
41
+ public:
42
+ ExpoImageProps() = default;
43
+ ExpoImageProps(const PropsParserContext &context,
44
+ const ExpoImageProps &sourceProps,
45
+ const RawProps &rawProps);
46
+
47
+ // expo-image resolves `source` to an array before it reaches native, the same
48
+ // shape React Native's <Image> uses, so the same conversion reads it.
49
+ ImageSources sources{};
50
+
51
+ // expo-image's `contentFit` is CSS object-fit rather than React Native's
52
+ // `resizeMode`, but every value it has maps onto one this platform already
53
+ // draws, so it is translated here rather than carried as a second vocabulary.
54
+ ImageResizeMode contentFit{ImageResizeMode::Cover};
55
+
56
+ SharedColor tintColor{};
57
+ };
58
+
59
+ // onLoadStart/onLoad/onError, matching the direct event types the view config
60
+ // declares. There is no onLoadEnd here because expo-image's JavaScript raises
61
+ // that one itself, from its own onLoad and onError handlers, and never listens
62
+ // for it natively. Not ImageEventEmitter: that one is bound to React
63
+ // Native's <Image> component and its payload shape, and expo-image's
64
+ // `onLoad` carries `{source: {url, width, height, mediaType}}` rather than
65
+ // `{source: {uri, ...}}`.
66
+ class ExpoImageEventEmitter : public ViewEventEmitter {
67
+ public:
68
+ using ViewEventEmitter::ViewEventEmitter;
69
+
70
+ void onLoadStart() const;
71
+ void onLoad(const ImageSource &source, double width, double height) const;
72
+ void onError(const std::string &message) const;
73
+ };
74
+
75
+ using ExpoImageShadowNode =
76
+ ConcreteViewShadowNode<ExpoImageComponentName, ExpoImageProps, ExpoImageEventEmitter>;
77
+
78
+ using ExpoImageComponentDescriptor = ConcreteComponentDescriptor<ExpoImageShadowNode>;
79
+
80
+ } // namespace facebook::react
@@ -0,0 +1,415 @@
1
+ #include "ExpoModules.h"
2
+
3
+ #include "JsRuntimeAccess.h"
4
+ #include "JsiPromise.h"
5
+ #include "PackageModules.h"
6
+ #include "PlatformServices.h"
7
+
8
+ #ifdef BASALT_HAS_EXPO
9
+ #include <EventEmitter.h>
10
+ #include <NativeModule.h>
11
+ #endif
12
+
13
+ #include <folly/json.h>
14
+ #include <glog/logging.h>
15
+ #include <jsi/JSIDynamic.h>
16
+
17
+ #include <filesystem>
18
+ #include <fstream>
19
+ #include <optional>
20
+ #include <sstream>
21
+
22
+ namespace basalt {
23
+
24
+ namespace {
25
+
26
+ // Parsed once, at load, so a malformed config is reported when it is read
27
+ // rather than when a module happens to ask for it.
28
+ std::optional<folly::dynamic> &appConfig() {
29
+ static std::optional<folly::dynamic> config;
30
+ return config;
31
+ }
32
+
33
+ } // namespace
34
+
35
+ void loadExpoAppConfigBeside(const std::string &bundlePath) {
36
+ std::error_code ignored;
37
+ const std::filesystem::path path =
38
+ std::filesystem::absolute(bundlePath, ignored).parent_path() / "app.config.json";
39
+
40
+ std::ifstream file(path);
41
+ if (!file) {
42
+ return;
43
+ }
44
+
45
+ std::ostringstream contents;
46
+ contents << file.rdbuf();
47
+
48
+ try {
49
+ folly::dynamic parsed = folly::parseJson(contents.str());
50
+ if (!parsed.isObject()) {
51
+ LOG(WARNING) << "expo app config at " << path.string() << " is not an object; ignoring it";
52
+ return;
53
+ }
54
+ appConfig() = std::move(parsed);
55
+ } catch (const std::exception &error) {
56
+ LOG(WARNING) << "could not read the expo app config at " << path.string() << ": "
57
+ << error.what();
58
+ }
59
+ }
60
+
61
+ std::string expoAppName() {
62
+ const auto &config = appConfig();
63
+ if (!config.has_value()) {
64
+ return {};
65
+ }
66
+ const folly::dynamic *name = config->get_ptr("name");
67
+ return name != nullptr && name->isString() ? name->getString() : std::string{};
68
+ }
69
+
70
+ #ifdef BASALT_HAS_EXPO
71
+
72
+ namespace {
73
+
74
+ using facebook::jsi::Array;
75
+ using facebook::jsi::Function;
76
+ using facebook::jsi::Object;
77
+ using facebook::jsi::PropNameID;
78
+ using facebook::jsi::Runtime;
79
+ using facebook::jsi::String;
80
+ using facebook::jsi::Value;
81
+
82
+ // A module is one of expo's own `NativeModule` instances rather than a plain
83
+ // object: that is where `addListener`, `removeListener` and `emit` come from,
84
+ // and expo-linking calls `addListener` whether or not anything ever emits.
85
+ void addFunction(Runtime &runtime,
86
+ Object &module,
87
+ const char *name,
88
+ unsigned argumentCount,
89
+ facebook::jsi::HostFunctionType function) {
90
+ module.setProperty(runtime,
91
+ name,
92
+ Function::createFromHostFunction(runtime,
93
+ PropNameID::forAscii(runtime, name),
94
+ argumentCount,
95
+ std::move(function)));
96
+ }
97
+
98
+ // ExpoClipboard.
99
+ //
100
+ // The same clipboard React Native's own `Clipboard` module uses; see
101
+ // core/PlatformServices.h. Text only: images and URLs are their own pasteboard
102
+ // types on each platform, and the methods for them are deliberately absent so
103
+ // that expo-clipboard's own check reports "not available on this platform"
104
+ // naming the method, rather than a stub returning null.
105
+ Object makeClipboardModule(Runtime &runtime) {
106
+ Object module = expoModule(runtime);
107
+
108
+ addFunction(runtime,
109
+ module,
110
+ "getStringAsync",
111
+ 1,
112
+ [](Runtime &rt, const Value &, const Value *, size_t) -> Value {
113
+ return resolved(rt, String::createFromUtf8(rt, clipboardText()));
114
+ });
115
+
116
+ addFunction(runtime,
117
+ module,
118
+ "setStringAsync",
119
+ 2,
120
+ [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
121
+ if (count < 1 || !args[0].isString()) {
122
+ return rejected(rt, "setStringAsync expects a string");
123
+ }
124
+ setClipboardText(args[0].asString(rt).utf8(rt));
125
+ // The boolean is "did it happen", which it did.
126
+ return resolved(rt, Value(true));
127
+ });
128
+
129
+ addFunction(runtime,
130
+ module,
131
+ "hasStringAsync",
132
+ 0,
133
+ [](Runtime &rt, const Value &, const Value *, size_t) -> Value {
134
+ return resolved(rt, Value(!clipboardText().empty()));
135
+ });
136
+
137
+ // iOS-only UI, and expo-clipboard reads it unconditionally on every platform.
138
+ module.setProperty(runtime, "isPasteButtonAvailable", Value(false));
139
+
140
+ return module;
141
+ }
142
+
143
+ // ExpoLinking.
144
+ //
145
+ // Only the two functions expo-linking calls on the native module; `openURL` and
146
+ // `canOpenURL` go through React Native's own `Linking`, which this platform
147
+ // already implements.
148
+ //
149
+ // Neither desktop delivers a URL to a running application yet -- macOS needs an
150
+ // Apple Event handler and a registered scheme, Linux a desktop entry and a
151
+ // single-instance activation -- so `getLinkingURL` is honestly null rather than
152
+ // absent: expo-linking calls it without checking, and `useURL()` returning null
153
+ // is exactly what "no deep link brought this app up" means.
154
+ Object makeLinkingModule(Runtime &runtime) {
155
+ Object module = expoModule(runtime);
156
+
157
+ addFunction(runtime,
158
+ module,
159
+ "getLinkingURL",
160
+ 0,
161
+ [](Runtime &, const Value &, const Value *, size_t) -> Value {
162
+ return Value::null();
163
+ });
164
+
165
+ addFunction(runtime,
166
+ module,
167
+ "clearInitialURL",
168
+ 0,
169
+ [](Runtime &, const Value &, const Value *, size_t) -> Value {
170
+ return Value::undefined();
171
+ });
172
+
173
+ return module;
174
+ }
175
+
176
+ // ExpoImage's module half: the static functions on `Image`, as against its view.
177
+ //
178
+ // The cache functions answer honestly rather than pretending. There is no disk
179
+ // cache here -- images are fetched through the same loader React Native's
180
+ // <Image> uses, which caches in memory for the life of the process -- so
181
+ // `clearDiskCacheAsync` reports false, which is expo-image's own way of saying
182
+ // "nothing was cleared". `prefetch` is not implemented rather than lying about
183
+ // having warmed a cache that does not exist.
184
+ //
185
+ // `ViewPrototypes` is what `requireNativeViewManager` reads to hang native view
186
+ // methods off a ref (`startAnimating`, `lockResourceAsync`). None of them apply
187
+ // to a still image drawn by this platform, so the object is there and empty --
188
+ // which is the difference between "this view has no extra methods" and the
189
+ // TypeError that reading a property of undefined would raise.
190
+ Object makeImageModule(Runtime &runtime) {
191
+ Object module = expoModule(runtime);
192
+
193
+ addFunction(runtime,
194
+ module,
195
+ "clearMemoryCache",
196
+ 0,
197
+ [](Runtime &rt, const Value &, const Value *, size_t) -> Value {
198
+ return resolved(rt, Value(false));
199
+ });
200
+
201
+ addFunction(runtime,
202
+ module,
203
+ "clearDiskCache",
204
+ 0,
205
+ [](Runtime &rt, const Value &, const Value *, size_t) -> Value {
206
+ return resolved(rt, Value(false));
207
+ });
208
+
209
+ module.setProperty(runtime, "ViewPrototypes", Object(runtime));
210
+
211
+ return module;
212
+ }
213
+
214
+ // ExponentConstants, which is expo-constants' native half.
215
+ //
216
+ // Two things live here. The manifest is the app's own Expo config, loaded from
217
+ // beside the bundle; expo-constants exposes it as `Constants.expoConfig`, and
218
+ // treats any object without a `metadata` key as an embedded manifest, which is
219
+ // what this is. Everything else is a native constant, and each one here is
220
+ // either true or absent -- expo-constants spreads this object into `Constants`,
221
+ // so a field invented here is a field an app will read and believe.
222
+ //
223
+ // `executionEnvironment: 'bare'` is the accurate answer: bare is "a native app
224
+ // that happens to use Expo modules", as against running inside Expo Go or a
225
+ // dev client, which a desktop host is not.
226
+ Object makeConstantsModule(Runtime &runtime) {
227
+ Object module = expoModule(runtime);
228
+
229
+ if (appConfig().has_value()) {
230
+ module.setProperty(
231
+ runtime, "manifest", facebook::jsi::valueFromDynamic(runtime, *appConfig()));
232
+ } else {
233
+ // Null rather than absent: expo-constants checks `ExponentConstants.manifest`
234
+ // and warns clearly when it is missing, which is the message a developer
235
+ // needs. Leaving the property off produces the same warning.
236
+ module.setProperty(runtime, "manifest", Value::null());
237
+ }
238
+
239
+ module.setProperty(runtime, "executionEnvironment", String::createFromUtf8(runtime, "bare"));
240
+ module.setProperty(runtime, "appOwnership", Value::null());
241
+ module.setProperty(runtime, "expoVersion", Value::null());
242
+ // A desktop is not a simulator, and nothing here reports a status bar.
243
+ module.setProperty(runtime, "isDevice", Value(true));
244
+ module.setProperty(runtime, "statusBarHeight", Value(0));
245
+ // Android and iOS fill this with their own device details. There is no shape
246
+ // defined for a desktop, and inventing one would be inventing a field.
247
+ module.setProperty(runtime, "platform", Object(runtime));
248
+
249
+ addFunction(runtime,
250
+ module,
251
+ "getWebViewUserAgentAsync",
252
+ 0,
253
+ [](Runtime &rt, const Value &, const Value *, size_t) -> Value {
254
+ return resolved(rt, Value::null());
255
+ });
256
+
257
+ return module;
258
+ }
259
+
260
+ // `expo.getViewConfig(moduleName, viewName)`, which is how an Expo view becomes
261
+ // a React Native component.
262
+ //
263
+ // `requireNativeViewManager` calls this, wraps the answer with React Native's
264
+ // `createViewConfig` -- which merges in the base view config, so style, layout
265
+ // and touch props come free -- and registers the result as a component called
266
+ // `ViewManagerAdapter_<moduleName>`. What is listed here is therefore exactly
267
+ // what reaches C++: a prop that is not in `validAttributes` is dropped in
268
+ // JavaScript, silently, before anything can diff it.
269
+ //
270
+ // Native platforms generate this from the module's own definition. Here it is a
271
+ // table, because here there is no module definition to generate it from -- the
272
+ // views are written directly against Fabric.
273
+ Value viewConfigFor(Runtime &runtime, const std::string &moduleName) {
274
+ if (moduleName != "ExpoImage") {
275
+ return Value::null();
276
+ }
277
+
278
+ Object attributes(runtime);
279
+ // What core/ExpoImageComponent.cpp parses. expo-image sends a good deal more
280
+ // -- placeholder, transition, blurhash, cachePolicy, SF Symbol props -- and
281
+ // leaving them out here is what stops them travelling to a platform that
282
+ // would ignore them anyway.
283
+ for (const char *name : {"source", "contentFit", "tintColor"}) {
284
+ attributes.setProperty(runtime, name, Value(true));
285
+ }
286
+
287
+ Object events(runtime);
288
+ // "topLoad" rather than "load": EventEmitter::normalizeEventType prefixes
289
+ // what C++ dispatches, and this is the name after that.
290
+ for (const auto &[eventName, handler] :
291
+ {std::pair{"topLoadStart", "onLoadStart"},
292
+ std::pair{"topLoad", "onLoad"},
293
+ std::pair{"topError", "onError"}}) {
294
+ Object registration(runtime);
295
+ registration.setProperty(runtime, "registrationName", String::createFromUtf8(runtime, handler));
296
+ events.setProperty(runtime, eventName, std::move(registration));
297
+ }
298
+
299
+ Object config(runtime);
300
+ config.setProperty(runtime, "validAttributes", std::move(attributes));
301
+ config.setProperty(runtime, "directEventTypes", std::move(events));
302
+ return Value(runtime, config);
303
+ }
304
+
305
+ } // namespace
306
+
307
+ Object expoModule(Runtime &runtime) {
308
+ return expo::NativeModule::createInstance(runtime);
309
+ }
310
+
311
+ void addExpoFunction(Runtime &runtime,
312
+ Object &module,
313
+ const char *name,
314
+ unsigned argumentCount,
315
+ facebook::jsi::HostFunctionType function) {
316
+ addFunction(runtime, module, name, argumentCount, std::move(function));
317
+ }
318
+
319
+ bool emitExpoEvent(const std::string &moduleName,
320
+ const std::string &eventName,
321
+ std::function<void(Runtime &, std::vector<Value> &)> arguments) {
322
+ return runOnJsRuntime([moduleName, eventName, arguments = std::move(arguments)](
323
+ Runtime &runtime) {
324
+ // Found by name each time; see the header for why nothing is held.
325
+ const Value expo = runtime.global().getProperty(runtime, "expo");
326
+ if (!expo.isObject()) {
327
+ return;
328
+ }
329
+ const Value modules = expo.asObject(runtime).getProperty(runtime, "modules");
330
+ if (!modules.isObject()) {
331
+ return;
332
+ }
333
+ const Value module =
334
+ modules.asObject(runtime).getProperty(runtime, moduleName.c_str());
335
+ if (!module.isObject()) {
336
+ // Installed later, or never. Not an error: see the header.
337
+ return;
338
+ }
339
+ try {
340
+ std::vector<Value> args;
341
+ arguments(runtime, args);
342
+ // A named lvalue: expo's emitEvent takes `jsi::Object &` in some versions
343
+ // and `const jsi::Object &` in others, and only an lvalue binds to both.
344
+ Object emitter = module.asObject(runtime);
345
+ expo::EventEmitter::emitEvent(runtime, emitter, eventName, args);
346
+ } catch (const facebook::jsi::JSIException &error) {
347
+ // A listener that throws is Expo's business -- its emitEvent swallows
348
+ // those deliberately. This is the payload itself failing to build, or the
349
+ // module turning out not to be an emitter, and the thread that asked for
350
+ // the emit is long gone and cannot be told.
351
+ //
352
+ // Caught at all because this runs as a scheduler task: an exception let
353
+ // out here unwinds through the runtime's own loop rather than through
354
+ // anything that could report it. JSIException rather than JSError, which
355
+ // is only one of its two halves.
356
+ LOG(ERROR) << "basalt: emitting " << moduleName << "." << eventName
357
+ << " failed: " << error.what();
358
+ }
359
+ });
360
+ }
361
+
362
+ void installExpoModules(Runtime &runtime, Object &modules) {
363
+ modules.setProperty(runtime, "ExpoClipboard", makeClipboardModule(runtime));
364
+ modules.setProperty(runtime, "ExpoImage", makeImageModule(runtime));
365
+ modules.setProperty(runtime, "ExpoLinking", makeLinkingModule(runtime));
366
+ // Replaces the empty stub installExpoRuntime puts in for Expo's own start-up.
367
+ modules.setProperty(runtime, "ExponentConstants", makeConstantsModule(runtime));
368
+
369
+
370
+ // And whatever capability packages the build discovered. Last, so that a
371
+ // package replacing a built-in is something you can see happening here rather
372
+ // than a name collision resolved by ordering nobody wrote down.
373
+ installPackageExpoModules(runtime, modules);
374
+ }
375
+
376
+ void installExpoViewConfigs(Runtime &runtime, Object &expo) {
377
+ expo.setProperty(
378
+ runtime,
379
+ "getViewConfig",
380
+ Function::createFromHostFunction(
381
+ runtime,
382
+ PropNameID::forAscii(runtime, "getViewConfig"),
383
+ 2,
384
+ [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
385
+ if (count < 1 || !args[0].isString()) {
386
+ return Value::null();
387
+ }
388
+ return viewConfigFor(rt, args[0].asString(rt).utf8(rt));
389
+ }));
390
+ }
391
+
392
+ #else
393
+
394
+ void installExpoModules(facebook::jsi::Runtime & /*runtime*/,
395
+ facebook::jsi::Object & /*modules*/) {}
396
+
397
+ void installExpoViewConfigs(facebook::jsi::Runtime & /*runtime*/,
398
+ facebook::jsi::Object & /*expo*/) {}
399
+
400
+ // Unlike expoModule and addExpoFunction above, this one has a no-op: a package's
401
+ // Expo module is compiled only when there is an Expo, but the native half that
402
+ // *reports* -- a subprocess reader, a device watcher -- is portable code with no
403
+ // reason to know whether Expo exists. It emits, and without an Expo nothing is
404
+ // listening, which is already the ordinary case.
405
+ bool emitExpoEvent(
406
+ const std::string & /*moduleName*/,
407
+ const std::string & /*eventName*/,
408
+ std::function<void(facebook::jsi::Runtime &, std::vector<facebook::jsi::Value> &)>
409
+ /*arguments*/) {
410
+ return false;
411
+ }
412
+
413
+ #endif
414
+
415
+ } // namespace basalt
@@ -0,0 +1,115 @@
1
+ // This platform's Expo modules, and the thing that was missing until now: a way
2
+ // to have one at all.
3
+ //
4
+ // `requireNativeModule('ExpoClipboard')` looks in three places, in order:
5
+ // `globalThis.expo.modules[name]`, the legacy bridge proxy, and a TurboModule
6
+ // of the same name wrapped by expo-modules-core. The third looks tempting --
7
+ // this project already has a TurboModule for the clipboard -- but the wrapper
8
+ // copies methods off `Object.getPrototypeOf(turboModule)`, and a C++ TurboModule
9
+ // is a jsi host object whose prototype is `Object.prototype`, so what it copies
10
+ // is nothing. The first is the real seam, and the one every native platform
11
+ // uses.
12
+ //
13
+ // So an Expo module here is a `expo.NativeModule` instance -- expo's own class,
14
+ // installed by ExpoRuntime, which is where `addListener` and the event
15
+ // machinery come from -- with host functions set on it. `moduleWithFunctions`
16
+ // below is the whole of the framework; everything else is one module's worth of
17
+ // behaviour, and each of them is a handful of lines because the behaviour is
18
+ // already written. `ExpoClipboard` is core/PlatformServices.h's clipboard, the
19
+ // same one React Native's own `Clipboard` module uses.
20
+ //
21
+ // **What a missing method does is part of the design.** Expo's JavaScript
22
+ // checks for each function before calling it -- `if (!ExpoClipboard.getImageAsync)
23
+ // throw new UnavailabilityError(...)` -- and reports "not available on this
24
+ // platform" naming the method. That is a better answer than a stub returning
25
+ // null, so a method this platform cannot implement is left off rather than
26
+ // faked.
27
+
28
+ #pragma once
29
+
30
+ #include <jsi/jsi.h>
31
+
32
+ #include <functional>
33
+ #include <string>
34
+ #include <vector>
35
+
36
+ namespace basalt {
37
+
38
+ // Adds this platform's Expo modules to `modules`, the object that becomes
39
+ // `globalThis.expo.modules`. Called from installExpoRuntime; there is no reason
40
+ // for anything else to call it.
41
+ //
42
+ // A no-op when the build was not pointed at an expo-modules-core, like
43
+ // everything else Expo here.
44
+ // The whole of the framework, exported because a capability that ships as its
45
+ // own package needs it to build its modules and should not copy it.
46
+ //
47
+ // `expoModule` is an `expo.NativeModule` instance -- expo's own class, installed
48
+ // by ExpoRuntime, which is where `addListener` and the event machinery come
49
+ // from. `addExpoFunction` puts a host function on one. Promises come from
50
+ // core/JsiPromise.h.
51
+ facebook::jsi::Object expoModule(facebook::jsi::Runtime &runtime);
52
+
53
+ void addExpoFunction(facebook::jsi::Runtime &runtime,
54
+ facebook::jsi::Object &module,
55
+ const char *name,
56
+ unsigned argumentCount,
57
+ facebook::jsi::HostFunctionType function);
58
+
59
+ void installExpoModules(facebook::jsi::Runtime &runtime, facebook::jsi::Object &modules);
60
+
61
+ // Emits an event on an already-installed Expo module, from any thread.
62
+ //
63
+ // This is the other half of `addExpoFunction`: a function is how JavaScript
64
+ // reaches native, and an event is how native reaches JavaScript later. Expo's
65
+ // own `NativeModule` is an `EventEmitter`, so `module.addListener('onOutput', f)`
66
+ // already works from JavaScript the moment the module is installed -- there was
67
+ // simply nothing here that ever emitted.
68
+ //
69
+ // **Named rather than captured.** The module is looked up as
70
+ // `globalThis.expo.modules[moduleName]` at the moment of the emit, rather than
71
+ // the caller holding the `jsi::Object` it built at install time. A held object
72
+ // outlives its runtime across a Fast Refresh reload and destroying it then
73
+ // touches a runtime that is gone; a name survives, and finds the new module
74
+ // installed into the new runtime. So a background thread can hold nothing but
75
+ // two strings, which is all a reader thread should have to be careful about.
76
+ //
77
+ // `arguments` builds the event payload and runs on the JavaScript thread, so it
78
+ // may use `runtime` freely. Everything it needs from the emitting thread it must
79
+ // capture by value.
80
+ //
81
+ // False when there is nothing to emit into: no host left a runtime runner (see
82
+ // JsRuntimeAccess.h), which is the tests and the probe. A module that is not
83
+ // installed, or is installed and has no listeners, is not a failure -- an event
84
+ // nobody is listening to is the normal case, and Expo's emitEvent does nothing.
85
+ bool emitExpoEvent(
86
+ const std::string &moduleName,
87
+ const std::string &eventName,
88
+ std::function<void(facebook::jsi::Runtime &, std::vector<facebook::jsi::Value> &)> arguments);
89
+
90
+ // Adds `expo.getViewConfig`, which is how expo-modules-core turns an Expo view
91
+ // into a React Native component. Separate from the modules because it goes on
92
+ // `expo` itself rather than on `expo.modules`.
93
+ void installExpoViewConfigs(facebook::jsi::Runtime &runtime, facebook::jsi::Object &expo);
94
+
95
+ // The app's Expo config -- `app.json` or `app.config.js`, resolved -- which is
96
+ // what `Constants.expoConfig` is and what expo-linking reads to find the app's
97
+ // URI scheme. Without it `Constants.expoConfig` is null and an app that reads
98
+ // `Constants.expoConfig.extra.something` crashes on the property access.
99
+ //
100
+ // iOS and Android embed it in the app bundle at build time, from a script Expo
101
+ // runs during the native build. This platform's equivalent moment is bundling:
102
+ // the bundler writes `app.config.json` beside the bundle, which is why this
103
+ // takes the bundle's path rather than the config's. Nothing being there is not
104
+ // an error -- a plain React Native app has no Expo config and never asks for
105
+ // one.
106
+ //
107
+ // Call before the runtime is installed; the hosts do it as they start.
108
+ void loadExpoAppConfigBeside(const std::string &bundlePath);
109
+
110
+ // The app's name from that config -- `name` in app.json -- or empty when there
111
+ // is no config or it names nothing. What a host titles its window with, rather
112
+ // than with its own name.
113
+ std::string expoAppName();
114
+
115
+ } // namespace basalt