UNPKG

expo-dev-launcher

Version:

Pre-release version of the Expo development launcher package for testing.

198 lines (193 loc) 10.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const config_plugins_1 = require("expo/config-plugins"); const pluginConfig_1 = require("./pluginConfig"); const pkg = require('../../package.json'); /** * Adds a build phase script that strips dev-launcher-specific local network permission keys * from non-Debug builds. This keeps the keys in Debug builds (where dev-launcher is active) * but removes only the dev-launcher entries from production builds. * * IMPORTANT: This script only removes _expo._tcp Bonjour services and the dev-launcher * usage description. Any other Bonjour services or custom local network descriptions * added by the app will be preserved in production builds. */ const withStripLocalNetworkKeysForRelease = (config) => { return (0, config_plugins_1.withXcodeProject)(config, (config) => { const project = config.modResults; const targetName = config.modRequest.projectName; const nativeTargetId = project.findTargetKey(targetName ?? ''); if (!nativeTargetId) { console.warn(`[Expo Dev Launcher] Could not find target "${targetName}" to add build phase script`); return config; } const buildPhaseName = '[Expo Dev Launcher] Strip Local Network Keys for Release'; const buildPhases = project.pbxNativeTargetSection()[nativeTargetId]?.buildPhases ?? []; const existingPhase = buildPhases.find((phase) => { return phase.comment === buildPhaseName; }); if (existingPhase) { return config; } project.addBuildPhase([], 'PBXShellScriptBuildPhase', buildPhaseName, nativeTargetId, { shellPath: '/bin/sh', shellScript: `# Strip dev-launcher-specific local network permission keys from non-Debug builds # This only removes _expo._tcp Bonjour services and the dev-launcher usage description. # Other Bonjour services and custom descriptions are preserved for production use. if [ "$CONFIGURATION" != "Debug" ]; then PLIST_PATH="\${TARGET_BUILD_DIR}/\${INFOPLIST_PATH}" if [ -f "$PLIST_PATH" ]; then # Check if NSBonjourServices exists if /usr/libexec/PlistBuddy -c "Print :NSBonjourServices" "$PLIST_PATH" >/dev/null 2>&1; then # Get the count of services COUNT=$(/usr/libexec/PlistBuddy -c "Print :NSBonjourServices" "$PLIST_PATH" 2>/dev/null | grep "^ " | wc -l | tr -d ' ') # Remove _expo._tcp for ((i=COUNT-1; i>=0; i--)); do SERVICE=$(/usr/libexec/PlistBuddy -c "Print :NSBonjourServices:$i" "$PLIST_PATH" 2>/dev/null || echo "") if echo "$SERVICE" | grep -q "_expo._tcp"; then /usr/libexec/PlistBuddy -c "Delete :NSBonjourServices:$i" "$PLIST_PATH" 2>/dev/null || true fi done # If the array is now empty, remove it entirely REMAINING=$(/usr/libexec/PlistBuddy -c "Print :NSBonjourServices" "$PLIST_PATH" 2>/dev/null | grep "^ " | wc -l | tr -d ' ') if [ "$REMAINING" -eq "0" ]; then /usr/libexec/PlistBuddy -c "Delete :NSBonjourServices" "$PLIST_PATH" 2>/dev/null || true fi fi # Only delete the description if it matches the dev-launcher default text DESC=$(/usr/libexec/PlistBuddy -c "Print :NSLocalNetworkUsageDescription" "$PLIST_PATH" 2>/dev/null || echo "") if echo "$DESC" | grep -q "Expo Dev Launcher"; then /usr/libexec/PlistBuddy -c "Delete :NSLocalNetworkUsageDescription" "$PLIST_PATH" 2>/dev/null || true fi fi fi `, }); const targetPhases = project.pbxNativeTargetSection()[nativeTargetId]?.buildPhases ?? []; const addedIdx = targetPhases.findIndex((p) => p.comment === buildPhaseName); if (addedIdx >= 0) { const [added] = targetPhases.splice(addedIdx, 1); if (added) { const firstEmbedIdx = targetPhases.findIndex((p) => /^Embed |^\[CP\] Embed /.test(p.comment ?? '')); const insertIdx = firstEmbedIdx >= 0 ? firstEmbedIdx : targetPhases.length; targetPhases.splice(insertIdx, 0, added); } } return config; }); }; /** * Adds the required Info.plist keys for local network permission. * Only adds _expo._tcp to the Bonjour services array and sets the usage description * if one doesn't already exist (preserving custom descriptions). */ const withLocalNetworkPermission = (config) => { return (0, config_plugins_1.withInfoPlist)(config, (config) => { const bonjourServices = config.modResults.NSBonjourServices ?? []; const hasExpoService = bonjourServices.some((service) => service.toLowerCase().replace(/\.$/, '') === '_expo._tcp'); if (!hasExpoService) { bonjourServices.push('_expo._tcp'); } config.modResults.NSBonjourServices = bonjourServices; if (!config.modResults.NSLocalNetworkUsageDescription) { config.modResults.NSLocalNetworkUsageDescription = 'Expo Dev Launcher uses the local network to discover and connect to development servers running on your computer.'; } return config; }); }; exports.default = (0, config_plugins_1.createRunOncePlugin)((config, props = {}) => { (0, pluginConfig_1.validateConfig)(props); const androidDefaultLaunchURL = props.android?.defaultLaunchURL ?? props.defaultLaunchURL; const iosDefaultLaunchURL = props.ios?.defaultLaunchURL ?? props.defaultLaunchURL; const iOSLaunchMode = props.ios?.launchMode ?? props.launchMode ?? props.ios?.launchModeExperimental ?? props.launchModeExperimental; config = (0, config_plugins_1.withInfoPlist)(config, (config) => { if (iOSLaunchMode === 'launcher') { config.modResults['DEV_CLIENT_TRY_TO_LAUNCH_LAST_BUNDLE'] = false; } if (iosDefaultLaunchURL) { config.modResults['DEV_CLIENT_DEFAULT_LAUNCHER_URL'] = iosDefaultLaunchURL; } return config; }); const iOSToolsButton = props.ios?.toolsButton ?? props.toolsButton; if (iOSToolsButton !== undefined) { config = (0, config_plugins_1.withInfoPlist)(config, (config) => { config.modResults['EXDevMenuShowFloatingActionButton'] = iOSToolsButton; return config; }); } const iOSEmbeddedBundle = props.ios?.embeddedBundle ?? props.embeddedBundle; if (iOSEmbeddedBundle) { config = (0, config_plugins_1.withInfoPlist)(config, (config) => { config.modResults['EXDevClientEmbeddedBundle'] = true; return config; }); } const iOSSkipOnboarding = props.ios?.skipOnboarding ?? props.skipOnboarding; if (iOSSkipOnboarding !== undefined) { config = (0, config_plugins_1.withInfoPlist)(config, (config) => { config.modResults['EXDevMenuIsOnboardingFinished'] = iOSSkipOnboarding; return config; }); } const iOSShowMenuAtLaunch = props.ios?.showMenuAtLaunch ?? props.showMenuAtLaunch; if (iOSShowMenuAtLaunch !== undefined) { config = (0, config_plugins_1.withInfoPlist)(config, (config) => { config.modResults['EXDevMenuShowsAtLaunch'] = iOSShowMenuAtLaunch; return config; }); } const androidLaunchMode = props.android?.launchMode ?? props.launchMode ?? props.android?.launchModeExperimental ?? props.launchModeExperimental; config = (0, config_plugins_1.withAndroidManifest)(config, (config) => { const mainApplication = config_plugins_1.AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults); if (androidLaunchMode === 'launcher') { config_plugins_1.AndroidConfig.Manifest.addMetaDataItemToMainApplication(mainApplication, 'DEV_CLIENT_TRY_TO_LAUNCH_LAST_BUNDLE', false?.toString()); } if (androidDefaultLaunchURL) { config_plugins_1.AndroidConfig.Manifest.addMetaDataItemToMainApplication(mainApplication, 'DEV_CLIENT_DEFAULT_LAUNCHER_URL', androidDefaultLaunchURL); } return config; }); const androidToolsButton = props.android?.toolsButton ?? props.toolsButton; if (androidToolsButton !== undefined) { config = (0, config_plugins_1.withAndroidManifest)(config, (config) => { const mainApplication = config_plugins_1.AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults); config_plugins_1.AndroidConfig.Manifest.addMetaDataItemToMainApplication(mainApplication, 'EXDevMenuShowFloatingActionButton', String(androidToolsButton)); return config; }); } const androidEmbeddedBundle = props.android?.embeddedBundle ?? props.embeddedBundle; if (androidEmbeddedBundle) { config = (0, config_plugins_1.withAndroidManifest)(config, (config) => { const mainApplication = config_plugins_1.AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults); config_plugins_1.AndroidConfig.Manifest.addMetaDataItemToMainApplication(mainApplication, 'EXDevClientEmbeddedBundle', String(true)); return config; }); } const androidSkipOnboarding = props.android?.skipOnboarding ?? props.skipOnboarding; if (androidSkipOnboarding !== undefined) { config = (0, config_plugins_1.withAndroidManifest)(config, (config) => { const mainApplication = config_plugins_1.AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults); config_plugins_1.AndroidConfig.Manifest.addMetaDataItemToMainApplication(mainApplication, 'EXDevMenuIsOnboardingFinished', String(androidSkipOnboarding)); return config; }); } const androidShowMenuAtLaunch = props.android?.showMenuAtLaunch ?? props.showMenuAtLaunch; if (androidShowMenuAtLaunch !== undefined) { config = (0, config_plugins_1.withAndroidManifest)(config, (config) => { const mainApplication = config_plugins_1.AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults); config_plugins_1.AndroidConfig.Manifest.addMetaDataItemToMainApplication(mainApplication, 'EXDevMenuShowsAtLaunch', String(androidShowMenuAtLaunch)); return config; }); } config = withLocalNetworkPermission(config); config = withStripLocalNetworkKeysForRelease(config); return config; }, pkg.name, pkg.version);