// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for details. import { join as pathJoin } from "path"; import * as XDLPackage from "xdl"; import * as MetroConfigPackage from "metro-config"; import { PackageLoader, PackageConfig } from "../../common/packageLoader"; import { removeModuleFromRequireCacheByName } from "../../common/utils"; import { SettingsHelper } from "../settingsHelper"; // Defensive helper: test environments or partial activation may load this module // before VS Code settings are fully available. Directly calling // SettingsHelper.getExpoDependencyVersion could throw if SettingsHelper is // undefined or method missing. We guard to return undefined (unversioned install) // instead of crashing the whole test run. function safeExpoVersion(packageName: string): string | undefined { try { if ( SettingsHelper && typeof (SettingsHelper as any).getExpoDependencyVersion === "function" ) { return (SettingsHelper as any).getExpoDependencyVersion(packageName); } } catch { // Swallow any unexpected errors; unversioned dependency is acceptable. } return undefined; } const XDL_PACKAGE = "xdl"; const METRO_CONFIG_PACKAGE = "@expo/metro-config"; const xdlPackageConfig = new PackageConfig(XDL_PACKAGE, safeExpoVersion(XDL_PACKAGE)); const metroConfigPackageConfig = new PackageConfig( METRO_CONFIG_PACKAGE, safeExpoVersion(METRO_CONFIG_PACKAGE), ); const ngrokPackageConfig = new PackageConfig( xdlPackageConfig.getPackageName(), xdlPackageConfig.getVersion(), "build/start/resolveNgrok", ); // There is the problem with '--no-save' flag for 'npm install' command for npm v6. // Installing npm dependencies with the `--no-save` flag will remove // other dependencies that were installed previously in the same manner (https://github.com/npm/cli/issues/1460). // So we should workaround it passing all packages for install to only one npm install command const EXPO_DEPS: PackageConfig[] = [xdlPackageConfig, metroConfigPackageConfig]; export const getXDLPackage: () => Promise = PackageLoader.getInstance().generateGetPackageFunction( xdlPackageConfig, ...EXPO_DEPS, ); export const getMetroConfigPackage: () => Promise = PackageLoader.getInstance().generateGetPackageFunction( metroConfigPackageConfig, ...EXPO_DEPS, ); export const getNgrokResolver: () => Promise = PackageLoader.getInstance().generateGetPackageFunction( ngrokPackageConfig, ...EXPO_DEPS, ); export type IUser = XDLPackage.IUser; export async function configReactNativeVersionWarnings(): Promise { const xdlPackage = await getXDLPackage(); if (xdlPackage.Config.validation !== undefined) { xdlPackage.Config.validation.reactNativeVersionWarnings = false; } } export async function attachLoggerStream( rootPath: string, options?: XDLPackage.IBunyanStream | any, ): Promise { (await getXDLPackage()).ProjectUtils.attachLoggerStream(rootPath, options); } export async function currentUser(): Promise { const xdl = await getXDLPackage(); return await (xdl.User ? xdl.User.getCurrentUserAsync() : xdl.UserManager.getCurrentUserAsync()); } export async function login(username: string, password: string): Promise { const xdl = await getXDLPackage(); return await (xdl.User ? xdl.User.loginAsync("user-pass", { username, password }) : xdl.UserManager.loginAsync("user-pass", { username, password, })); } export async function getExpoSdkVersions(): Promise { return (await getXDLPackage()).Versions.sdkVersionsAsync(); } export async function getReleasedExpoSdkVersions(): Promise { return (await getXDLPackage()).Versions.releasedSdkVersionsAsync(); } export async function publish( projectRoot: string, options?: XDLPackage.IPublishOptions, ): Promise { return (await getXDLPackage()).Project.publishAsync(projectRoot, options); } export async function setOptions(projectRoot: string, options: XDLPackage.IOptions): Promise { await (await getXDLPackage()).ProjectSettings.setPackagerInfoAsync(projectRoot, options); } export async function startExponentServer(projectRoot: string): Promise { await (await getXDLPackage()).Project.startExpoServerAsync(projectRoot); } export async function startTunnels(projectRoot: string): Promise { await (await getXDLPackage()).Project.startTunnelsAsync(projectRoot); } export async function getUrl( projectRoot: string, options?: XDLPackage.IUrlOptions, ): Promise { return (await getXDLPackage()).UrlUtils.constructManifestUrlAsync(projectRoot, options); } export async function stopAll(projectRoot: string): Promise { await (await getXDLPackage()).Project.stopAsync(projectRoot); } export async function startAdbReverse(projectRoot: string): Promise { return (await getXDLPackage()).Android.startAdbReverseAsync(projectRoot); } export async function stopAdbReverse(projectRoot: string): Promise { await (await getXDLPackage()).Android.stopAdbReverseAsync(projectRoot); } export async function getMetroConfig( projectRoot: string, ): Promise { return (await getMetroConfigPackage()).loadAsync(projectRoot); } export async function isNgrokInstalled(projectRoot: string): Promise { const ngrokResolver = await getNgrokResolver(); try { const ngrok = await ngrokResolver.resolveNgrokAsync(projectRoot, { shouldPrompt: false, autoInstall: false, }); return !!ngrok; } catch (err) { // If unsupported version of the "@expo/ngrok" package was detected, we need to update the package. // Since the "require" method used to parse the "ngrok⁄package.json" file in the "xdl" package caches // all processed modules, we have to remove this file from cache to be able to require a new version // of that file after the update of the "@expo/ngrok" package removeModuleFromRequireCacheByName(pathJoin("ngrok", "package.json")); throw err; } }