import { ExpoConfig } from '@expo/config-types' ; import { JSONObject } from '@expo/json-file' ; import chalk from 'chalk' ; import { boolish } from 'getenv' ; import { ExportedConfig , ExportedConfigWithProps , Mod , ModPlatform } from '../Plugin.types' ; import { PluginError } from '../utils/errors' ; const EXPO_DEBUG = boolish ( 'EXPO_DEBUG' , false ) ; export type BaseModOptions = { platform : ModPlatform ; mod : string ; isProvider ? : boolean ; skipEmptyMod ? : boolean ; saveToInternal ? : boolean ; /**
     * If the mod supports introspection, and avoids making any filesystem modifications during compilation.
     * By enabling, this mod, and all of its descendants will be run in introspection mode.
     * This should only be used for static files like JSON or XML, and not for application files that require regexes,
     * or complex static files that require other files to be generated like Xcode `.pbxproj`.
     */ isIntrospective ? : boolean ; } ; param config param platform param mod param skipEmptyMod param saveToInternal param isProvider param action export function withBaseMod < T > ( config , { platform , mod , action , skipEmptyMod , isProvider , isIntrospective , saveToInternal , } ) { if ( ! config . mods ) { config . mods = { } ; } if ( ! config . mods [ platform ] ) { config . mods [ platform ] = { } ; } let interceptedMod = ( config . mods [ platform ] as Record < string , any > ) [ mod ] ; if ( ! interceptedMod ) { if ( skipEmptyMod ) { return config ; } const noopMod = config => config ; interceptedMod = noopMod ; } let debugTrace = '' ; const isDebug = config . _internal ?. isDebug ?? EXPO_DEBUG ; if ( isDebug ) { const stack = new Error ( ) . stack ; debugTrace = getDebugPluginStackFromStackTrace ( stack ) ; const modStack = chalk . bold ( `${ platform }.${ mod }` ) ; debugTrace = `${ modStack }: ${ debugTrace }` ; } if ( interceptedMod . isProvider ) { if ( isProvider ) { throw new PluginError ( `Cannot set provider mod for "${ platform }.${ mod }" because another is already being used.` , 'CONFLICTING_PROVIDER' ) ; } else { throw new PluginError ( `Cannot add mod to "${ platform }.${ mod }" because the provider has already been added. Provider must be the last mod added.` , 'INVALID_MOD_ORDER' ) ; } } async function interceptingMod ( { modRequest , ... config } ) { if ( isDebug ) { console . log ( debugTrace ) ; } const results = await action ( { ... config , modRequest : { ... modRequest , nextMod : interceptedMod } } ) ; if ( saveToInternal ) { saveToInternalObject ( results , platform , mod , ( results . modResults as unknown ) as JSONObject ) ; } return results ; } interceptingMod . isProvider = isProvider ; if ( isIntrospective ) { interceptingMod . isIntrospective = isIntrospective ; } ( config . mods [ platform ] as any ) [ mod ] = interceptingMod ; return config ; } function saveToInternalObject ( config , platformName , modName , results ) { if ( ! config . _internal ) config . _internal = { } ; if ( ! config . _internal . modResults ) config . _internal . modResults = { } ; if ( ! config . _internal . modResults [ platformName ] ) config . _internal . modResults [ platformName ] = { } ; config . _internal . modResults [ platformName ] [ modName ] = results ; } function getDebugPluginStackFromStackTrace ( stacktrace ? ) { if ( ! stacktrace ) { return '' ; } const treeStackLines = [ ] ; for ( const line of stacktrace . split ( '\n' ) ) { const [ first , second ] = line . trim ( ) . split ( ' ' ) ; if ( first === 'at' ) { treeStackLines . push ( second ) ; } } const plugins = treeStackLines . map ( first => { return ( first ?. match ( /^(\bwith[A-Z].*?\b)/ ) ?. [ 1 ] ?. trim ( ) ?? first ?. match ( /\.(\bwith[A-Z].*?\b)/ ) ?. [ 1 ] ?. trim ( ) ?? null ) ; } ) . filter ( Boolean ) . filter ( plugin => { return ! [ 'withMod' , 'withBaseMod' , 'withExtendedMod' ] . includes ( plugin ! ) ; } ) ; const commonPlugins = [ 'withPlugins' , 'withRunOnce' , 'withStaticPlugin' ] ; return ( ( plugins as string [ ] ) . reverse ( ) . map ( ( pluginName , index ) => { if ( pluginName . includes ( 'BaseMod' ) ) { pluginName = chalk . bold ( pluginName ) ; } if ( pluginName . toLowerCase ( ) . includes ( 'dangerous' ) ) { pluginName = chalk . red ( pluginName ) ; } if ( index === 0 ) { return chalk . blue ( pluginName ) ; } else if ( commonPlugins . includes ( pluginName ) ) { return chalk . dim ( pluginName ) ; } return pluginName ; } ) . join ( ' ➜ ' ) ) ; } param config param platform param mod param action export function withMod < T > ( config , { platform , mod , action , } ) { return withBaseMod ( config , { platform , mod , isProvider : false , async action ( { modRequest : { nextMod , ... modRequest } , modResults , ... config } ) { const results = await action ( { modRequest , modResults : modResults as T , ... config } ) ; return nextMod ! ( results as any ) ; } } ) ; }