import { Capability , getKitCapabilities , getKitConfig , KitConfig , KitType , } from "@rnx-kit/config" ; import { error , warn } from "@rnx-kit/console" ; import { PackageManifest , findPackageDependencyDir , parsePackageRef , readPackage , } from "@rnx-kit/tools-node/package" ; import { concatVersionRanges } from "./helpers" ; import { getProfilesFor , getProfileVersionsFor , profilesSatisfying , } from "./profiles" ; import type { CheckOptions , Profile , ProfileVersion } from "./types" ; type Requirements = Required < Pick < KitConfig , "reactNativeVersion" | "capabilities" > > ; type Trace = { module : string ; reactNativeVersion : string ; profiles : ProfileVersion [ ] ; } ; function isCoreCapability ( capability ) { return capability . startsWith ( "core-" ) ; } function isDevOnlyCapability ( capability , profiles ) { return profiles . some ( ( profile ) => profile [ capability ] ?. devOnly ) ; } export function visitDependencies ( { dependencies } , projectRoot , visitor , visited = new Set < string > ( ) ) { if ( ! dependencies ) { return ; } Object . keys ( dependencies ) . forEach ( ( dependency ) => { if ( visited . has ( dependency ) ) { return ; } visited . add ( dependency ) ; const packageRef = parsePackageRef ( dependency ) ; const packageDir = findPackageDependencyDir ( packageRef , { startDir : projectRoot } ) ; if ( ! packageDir ) { warn ( `Unable to resolve module '${ dependency }'` ) ; return ; } visitor ( dependency , packageDir ) ; const manifest = readPackage ( packageDir ) ; visitDependencies ( manifest , packageDir , visitor , visited ) ; } ) ; } export function getRequirements ( targetReactNativeVersion , kitType , targetManifest , projectRoot , customProfiles , { loose } ) { let profileVersions = getProfileVersionsFor ( targetReactNativeVersion ) ; if ( profileVersions . length === 0 ) { throw new Error ( `No profile could satisfy React Native version: ${ targetReactNativeVersion }` ) ; } const allCapabilities = new Set < Capability > ( ) ; if ( kitType === "app" ) { const trace = [ { module : targetManifest . name , reactNativeVersion : targetReactNativeVersion , profiles : profileVersions } , ] ; visitDependencies ( targetManifest , projectRoot , ( module , modulePath ) => { const kitConfig = getKitConfig ( { cwd : modulePath } ) ; if ( ! kitConfig ) { return ; } const { reactNativeVersion , capabilities } = getKitCapabilities ( kitConfig ) ; const validVersions = profilesSatisfying ( profileVersions , reactNativeVersion ) ; if ( validVersions . length != profileVersions . length ) { trace . push ( { module , reactNativeVersion , profiles : validVersions } ) ; } if ( Array . isArray ( capabilities ) ) { capabilities . forEach ( ( capability ) => allCapabilities . add ( capability ) ) ; } if ( validVersions . length > 0 ) { profileVersions = validVersions ; } } ) ; if ( trace [ trace . length - 1 ] . profiles . length === 0 ) { const message = "No React Native profile could satisfy all dependencies" ; const fullTrace = [ message , ... trace . map ( ( { module , reactNativeVersion , profiles } ) => { const satisfiedVersions = profiles . join ( ", " ) ; return `    [${ satisfiedVersions }] satisfies '${ module }' because it supports '${ reactNativeVersion }'` ; } ) , ] . join ( "\n" ) ; if ( loose ) { warn ( fullTrace ) ; } else { error ( fullTrace ) ; throw new Error ( message ) ; } } const profiles = getProfilesFor ( profileVersions , customProfiles ) ; allCapabilities . forEach ( ( capability ) => { /**
             * Core capabilities are capabilities that must always be declared by the
             * hosting app and should not be included when gathering requirements.
             * This is to avoid forcing an app to install dependencies it does not
             * need, e.g. `react-native-windows` when the app only supports iOS.
             */ if ( isCoreCapability ( capability ) || isDevOnlyCapability ( capability , profiles ) ) { allCapabilities . delete ( capability ) ; } } ) ; } return { reactNativeVersion : concatVersionRanges ( profileVersions ) , capabilities : Array . from ( allCapabilities ) } ; }