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.
- package/LICENSE +21 -0
- package/dist/cli/basalt.d.ts +3 -0
- package/dist/cli/basalt.d.ts.map +1 -0
- package/dist/cli/basalt.js +47 -0
- package/dist/cli/basalt.js.map +1 -0
- package/dist/cli/bundle-cli.d.ts +12 -0
- package/dist/cli/bundle-cli.d.ts.map +1 -0
- package/dist/cli/bundle-cli.js +124 -0
- package/dist/cli/bundle-cli.js.map +1 -0
- package/dist/cli/bundleWithAssets.d.ts +79 -0
- package/dist/cli/bundleWithAssets.d.ts.map +1 -0
- package/dist/cli/bundleWithAssets.js +198 -0
- package/dist/cli/bundleWithAssets.js.map +1 -0
- package/dist/cli/desktop.d.ts +339 -0
- package/dist/cli/desktop.d.ts.map +1 -0
- package/dist/cli/desktop.js +1029 -0
- package/dist/cli/desktop.js.map +1 -0
- package/dist/cli/doctor.d.ts +31 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +283 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/init.d.ts +77 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +442 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/metro.d.ts +49 -0
- package/dist/cli/metro.d.ts.map +1 -0
- package/dist/cli/metro.js +148 -0
- package/dist/cli/metro.js.map +1 -0
- package/dist/cli/packageApp.d.ts +102 -0
- package/dist/cli/packageApp.d.ts.map +1 -0
- package/dist/cli/packageApp.js +319 -0
- package/dist/cli/packageApp.js.map +1 -0
- package/dist/cli/steps.d.ts +34 -0
- package/dist/cli/steps.d.ts.map +1 -0
- package/dist/cli/steps.js +53 -0
- package/dist/cli/steps.js.map +1 -0
- package/dist/metro-config.d.ts +168 -0
- package/dist/metro-config.d.ts.map +1 -0
- package/dist/metro-config.js +671 -0
- package/dist/metro-config.js.map +1 -0
- package/dist/react-native.config.d.ts +7 -0
- package/dist/react-native.config.d.ts.map +1 -0
- package/dist/react-native.config.js +80 -0
- package/dist/react-native.config.js.map +1 -0
- package/dist/src/DragSource.d.ts +52 -0
- package/dist/src/DragSource.d.ts.map +1 -0
- package/dist/src/DragSource.js +97 -0
- package/dist/src/DragSource.js.map +1 -0
- package/dist/src/DropTarget.d.ts +58 -0
- package/dist/src/DropTarget.d.ts.map +1 -0
- package/dist/src/DropTarget.js +131 -0
- package/dist/src/DropTarget.js.map +1 -0
- package/dist/src/KeyHandler.d.ts +57 -0
- package/dist/src/KeyHandler.d.ts.map +1 -0
- package/dist/src/KeyHandler.js +48 -0
- package/dist/src/KeyHandler.js.map +1 -0
- package/dist/src/Menu.d.ts +72 -0
- package/dist/src/Menu.d.ts.map +1 -0
- package/dist/src/Menu.js +179 -0
- package/dist/src/Menu.js.map +1 -0
- package/dist/src/TitleBar.d.ts +75 -0
- package/dist/src/TitleBar.d.ts.map +1 -0
- package/dist/src/TitleBar.js +236 -0
- package/dist/src/TitleBar.js.map +1 -0
- package/dist/src/Window.d.ts +87 -0
- package/dist/src/Window.d.ts.map +1 -0
- package/dist/src/Window.js +248 -0
- package/dist/src/Window.js.map +1 -0
- package/dist/src/closeRequest.d.ts +55 -0
- package/dist/src/closeRequest.d.ts.map +1 -0
- package/dist/src/closeRequest.js +134 -0
- package/dist/src/closeRequest.js.map +1 -0
- package/dist/src/index.d.ts +45 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +92 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/overrides/Alert.d.ts +69 -0
- package/dist/src/overrides/Alert.d.ts.map +1 -0
- package/dist/src/overrides/Alert.js +112 -0
- package/dist/src/overrides/Alert.js.map +1 -0
- package/dist/src/overrides/BaseViewConfig.d.ts +28 -0
- package/dist/src/overrides/BaseViewConfig.d.ts.map +1 -0
- package/dist/src/overrides/BaseViewConfig.js +72 -0
- package/dist/src/overrides/BaseViewConfig.js.map +1 -0
- package/dist/src/overrides/Platform.linux.d.ts +10 -0
- package/dist/src/overrides/Platform.linux.d.ts.map +1 -0
- package/dist/src/overrides/Platform.linux.js +15 -0
- package/dist/src/overrides/Platform.linux.js.map +1 -0
- package/dist/src/overrides/Platform.macos.d.ts +10 -0
- package/dist/src/overrides/Platform.macos.d.ts.map +1 -0
- package/dist/src/overrides/Platform.macos.js +15 -0
- package/dist/src/overrides/Platform.macos.js.map +1 -0
- package/dist/src/overrides/Platform.windows.d.ts +10 -0
- package/dist/src/overrides/Platform.windows.d.ts.map +1 -0
- package/dist/src/overrides/Platform.windows.js +15 -0
- package/dist/src/overrides/Platform.windows.js.map +1 -0
- package/dist/src/overrides/ReactDevToolsSettingsManager.d.ts +24 -0
- package/dist/src/overrides/ReactDevToolsSettingsManager.d.ts.map +1 -0
- package/dist/src/overrides/ReactDevToolsSettingsManager.js +30 -0
- package/dist/src/overrides/ReactDevToolsSettingsManager.js.map +1 -0
- package/dist/src/overrides/RefreshControl.d.ts +52 -0
- package/dist/src/overrides/RefreshControl.d.ts.map +1 -0
- package/dist/src/overrides/RefreshControl.js +92 -0
- package/dist/src/overrides/RefreshControl.js.map +1 -0
- package/dist/src/overrides/ScrollView.d.ts +39 -0
- package/dist/src/overrides/ScrollView.d.ts.map +1 -0
- package/dist/src/overrides/ScrollView.js +59 -0
- package/dist/src/overrides/ScrollView.js.map +1 -0
- package/dist/src/overrides/Share.d.ts +96 -0
- package/dist/src/overrides/Share.d.ts.map +1 -0
- package/dist/src/overrides/Share.js +97 -0
- package/dist/src/overrides/Share.js.map +1 -0
- package/dist/src/overrides/TextInput.d.ts +81 -0
- package/dist/src/overrides/TextInput.d.ts.map +1 -0
- package/dist/src/overrides/TextInput.js +213 -0
- package/dist/src/overrides/TextInput.js.map +1 -0
- package/dist/src/overrides/View.d.ts +36 -0
- package/dist/src/overrides/View.d.ts.map +1 -0
- package/dist/src/overrides/View.js +99 -0
- package/dist/src/overrides/View.js.map +1 -0
- package/dist/src/overrides/createPlatform.d.ts +32 -0
- package/dist/src/overrides/createPlatform.d.ts.map +1 -0
- package/dist/src/overrides/createPlatform.js +84 -0
- package/dist/src/overrides/createPlatform.js.map +1 -0
- package/dist/src/overrides/setUpDeveloperTools.d.ts +28 -0
- package/dist/src/overrides/setUpDeveloperTools.d.ts.map +1 -0
- package/dist/src/overrides/setUpDeveloperTools.js +84 -0
- package/dist/src/overrides/setUpDeveloperTools.js.map +1 -0
- package/dist/src/overrides/setUpXHR.d.ts +46 -0
- package/dist/src/overrides/setUpXHR.d.ts.map +1 -0
- package/dist/src/overrides/setUpXHR.js +236 -0
- package/dist/src/overrides/setUpXHR.js.map +1 -0
- package/dist/src/quitRequest.d.ts +55 -0
- package/dist/src/quitRequest.d.ts.map +1 -0
- package/dist/src/quitRequest.js +121 -0
- package/dist/src/quitRequest.js.map +1 -0
- package/dist/src/resourcePath.d.ts +18 -0
- package/dist/src/resourcePath.d.ts.map +1 -0
- package/dist/src/resourcePath.js +31 -0
- package/dist/src/resourcePath.js.map +1 -0
- package/dist/src/responseBody.d.ts +76 -0
- package/dist/src/responseBody.d.ts.map +1 -0
- package/dist/src/responseBody.js +94 -0
- package/dist/src/responseBody.js.map +1 -0
- package/dist/src/responseHeaders.d.ts +40 -0
- package/dist/src/responseHeaders.d.ts.map +1 -0
- package/dist/src/responseHeaders.js +75 -0
- package/dist/src/responseHeaders.js.map +1 -0
- package/dist/src/titleBarState.d.ts +43 -0
- package/dist/src/titleBarState.d.ts.map +1 -0
- package/dist/src/titleBarState.js +77 -0
- package/dist/src/titleBarState.js.map +1 -0
- package/dist/src/useContextMenu.d.ts +113 -0
- package/dist/src/useContextMenu.d.ts.map +1 -0
- package/dist/src/useContextMenu.js +109 -0
- package/dist/src/useContextMenu.js.map +1 -0
- package/dist/src/useDialog.d.ts +98 -0
- package/dist/src/useDialog.d.ts.map +1 -0
- package/dist/src/useDialog.js +112 -0
- package/dist/src/useDialog.js.map +1 -0
- package/dist/src/useDisplays.d.ts +70 -0
- package/dist/src/useDisplays.d.ts.map +1 -0
- package/dist/src/useDisplays.js +118 -0
- package/dist/src/useDisplays.js.map +1 -0
- package/dist/src/useHandledKeys.d.ts +38 -0
- package/dist/src/useHandledKeys.d.ts.map +1 -0
- package/dist/src/useHandledKeys.js +70 -0
- package/dist/src/useHandledKeys.js.map +1 -0
- package/dist/src/useWindow.d.ts +140 -0
- package/dist/src/useWindow.d.ts.map +1 -0
- package/dist/src/useWindow.js +233 -0
- package/dist/src/useWindow.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/native/CMakeLists.txt +355 -0
- package/native/bootstrap.sh +613 -0
- package/native/cmake/CompilerFlags.cmake +49 -0
- package/native/cmake/Expo.cmake +125 -0
- package/native/cmake/ReactNativeCore.cmake +612 -0
- package/native/cmake/Reanimated.cmake +108 -0
- package/native/cmake/Skia.cmake +379 -0
- package/native/cmake/ThirdParty.cmake +290 -0
- package/native/cmake/Worklets.cmake +67 -0
- package/native/cmake/codegen/CMakeLists.txt +32 -0
- package/native/core/AppIdentity.cpp +85 -0
- package/native/core/AppIdentity.h +59 -0
- package/native/core/AppPaths.cpp +96 -0
- package/native/core/AppPaths.h +53 -0
- package/native/core/AppearanceModule.cpp +60 -0
- package/native/core/AppearanceModule.h +40 -0
- package/native/core/Backface.h +59 -0
- package/native/core/BlobModule.cpp +283 -0
- package/native/core/BlobModule.h +76 -0
- package/native/core/BlobRegistry.cpp +75 -0
- package/native/core/BlobRegistry.h +44 -0
- package/native/core/ColorScheme.cpp +105 -0
- package/native/core/ColorScheme.h +59 -0
- package/native/core/ComponentRegistry.h +30 -0
- package/native/core/ControlMetrics.h +37 -0
- package/native/core/CoreModules.cpp +592 -0
- package/native/core/CoreModules.h +308 -0
- package/native/core/DebuggingOverlay.cpp +106 -0
- package/native/core/DebuggingOverlay.h +65 -0
- package/native/core/DesktopControls.cpp +232 -0
- package/native/core/DesktopControls.h +170 -0
- package/native/core/DevBundle.cpp +108 -0
- package/native/core/DevBundle.h +69 -0
- package/native/core/DevMenu.cpp +99 -0
- package/native/core/DevMenu.h +75 -0
- package/native/core/DialogModule.cpp +150 -0
- package/native/core/DialogModule.h +51 -0
- package/native/core/DragAndDrop.cpp +45 -0
- package/native/core/DragAndDrop.h +225 -0
- package/native/core/ExpoImageComponent.cpp +79 -0
- package/native/core/ExpoImageComponent.h +80 -0
- package/native/core/ExpoModules.cpp +415 -0
- package/native/core/ExpoModules.h +115 -0
- package/native/core/ExpoRuntime.cpp +366 -0
- package/native/core/ExpoRuntime.h +42 -0
- package/native/core/FocusRing.h +36 -0
- package/native/core/FontRegistry.h +51 -0
- package/native/core/GestureHandlerModule.cpp +136 -0
- package/native/core/GestureHandlerModule.h +71 -0
- package/native/core/Gestures.cpp +533 -0
- package/native/core/Gestures.h +145 -0
- package/native/core/HoverTracker.cpp +69 -0
- package/native/core/HoverTracker.h +109 -0
- package/native/core/HttpClient.cpp +282 -0
- package/native/core/ImageBytes.cpp +205 -0
- package/native/core/ImageBytes.h +29 -0
- package/native/core/ImageCache.cpp +51 -0
- package/native/core/ImageCache.h +89 -0
- package/native/core/JsRuntimeAccess.cpp +70 -0
- package/native/core/JsRuntimeAccess.h +60 -0
- package/native/core/JsiPromise.h +51 -0
- package/native/core/KeyEvents.cpp +116 -0
- package/native/core/KeyEvents.h +144 -0
- package/native/core/LogBoxSurface.cpp +46 -0
- package/native/core/LogBoxSurface.h +68 -0
- package/native/core/MenuModel.h +117 -0
- package/native/core/MenuModule.cpp +209 -0
- package/native/core/MenuModule.h +66 -0
- package/native/core/MountingWalk.h +524 -0
- package/native/core/PackageModules.cpp.in +25 -0
- package/native/core/PackageModules.h +50 -0
- package/native/core/PlatformConstantsModule.cpp +63 -0
- package/native/core/PlatformConstantsModule.h +64 -0
- package/native/core/PlatformServices.h +213 -0
- package/native/core/PointerButtons.h +63 -0
- package/native/core/PullToRefresh.cpp +57 -0
- package/native/core/PullToRefresh.h +73 -0
- package/native/core/ReanimatedCompat.h +47 -0
- package/native/core/ReanimatedModule.cpp +203 -0
- package/native/core/ReanimatedModule.h +55 -0
- package/native/core/ScrollAnimation.cpp +59 -0
- package/native/core/ScrollAnimation.h +70 -0
- package/native/core/ScrollBounds.cpp +26 -0
- package/native/core/ScrollBounds.h +68 -0
- package/native/core/ScrollIndicator.cpp +50 -0
- package/native/core/ScrollIndicator.h +69 -0
- package/native/core/ScrollMomentum.cpp +71 -0
- package/native/core/ScrollMomentum.h +73 -0
- package/native/core/ScrollSnap.cpp +123 -0
- package/native/core/ScrollSnap.h +80 -0
- package/native/core/ShareFallback.cpp +101 -0
- package/native/core/ShareFallback.h +45 -0
- package/native/core/SkiaPictureViewComponent.cpp +11 -0
- package/native/core/SkiaPictureViewComponent.h +47 -0
- package/native/core/SourceCodeModule.cpp +55 -0
- package/native/core/SourceCodeModule.h +53 -0
- package/native/core/StatusBarModule.cpp +23 -0
- package/native/core/StatusBarModule.h +42 -0
- package/native/core/TestDialog.cpp +148 -0
- package/native/core/TestDialog.h +96 -0
- package/native/core/TestKeys.cpp +78 -0
- package/native/core/TestKeys.h +50 -0
- package/native/core/TestQuitFile.cpp +33 -0
- package/native/core/TestQuitFile.h +60 -0
- package/native/core/TestSettle.h +192 -0
- package/native/core/TitleBarRegions.h +31 -0
- package/native/core/TurboModuleProxy.cpp +51 -0
- package/native/core/TurboModuleProxy.h +46 -0
- package/native/core/UIManagerAccess.cpp +43 -0
- package/native/core/UIManagerAccess.h +43 -0
- package/native/core/WindowBoundsCache.cpp +319 -0
- package/native/core/WindowControl.h +235 -0
- package/native/core/WindowHost.h +158 -0
- package/native/core/WindowMethods.cpp +167 -0
- package/native/core/WindowMethods.h +84 -0
- package/native/core/WindowsModule.cpp +448 -0
- package/native/core/WindowsModule.h +133 -0
- package/native/core/WorkletsModule.cpp +199 -0
- package/native/core/WorkletsModule.h +57 -0
- package/native/core/portability_probe.cpp +210 -0
- package/native/tests/EventRecorder.h +124 -0
- package/native/tests/TestHarness.cpp +109 -0
- package/native/tests/TestHarness.h +89 -0
- package/native/tests/TreeDump.h +49 -0
- package/native/tests/test_app_identity.cpp +114 -0
- package/native/tests/test_app_paths.cpp +48 -0
- package/native/tests/test_appearance.cpp +107 -0
- package/native/tests/test_backface.cpp +79 -0
- package/native/tests/test_blobs.cpp +157 -0
- package/native/tests/test_controls.cpp +185 -0
- package/native/tests/test_devbundle.cpp +103 -0
- package/native/tests/test_drag_and_drop.cpp +143 -0
- package/native/tests/test_gestures.cpp +331 -0
- package/native/tests/test_hover.cpp +92 -0
- package/native/tests/test_image_cache.cpp +109 -0
- package/native/tests/test_js_runtime_access.cpp +137 -0
- package/native/tests/test_key_events.cpp +235 -0
- package/native/tests/test_momentum.cpp +138 -0
- package/native/tests/test_quit_file.cpp +104 -0
- package/native/tests/test_scroll_animation.cpp +120 -0
- package/native/tests/test_scroll_bounds.cpp +66 -0
- package/native/tests/test_scroll_indicator.cpp +149 -0
- package/native/tests/test_scroll_snap.cpp +112 -0
- package/native/tests/test_settle.cpp +115 -0
- package/native/tests/test_share.cpp +75 -0
- package/native/tests/test_sourcecode.cpp +62 -0
- package/native/tests/test_window_close.cpp +119 -0
- package/native/tests/test_window_limits.cpp +123 -0
- package/package.json +43 -0
- package/react-native.config.js +86 -0
- 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
|