import Debug from 'debug' ; import { ConfigPlugin , ExportedConfig , ExportedConfigWithProps , ModPlatform , } from '../Plugin.types' ; import { BaseModOptions , withBaseMod } from './withMod' ; const debug = Debug ( 'expo:config-plugins:base-mods' ) ; export type ForwardedBaseModOptions = Partial < Pick < BaseModOptions , 'saveToInternal' | 'skipEmptyMod' > > ; export type BaseModProviderMethods < ModType , Props extends ForwardedBaseModOptions = ForwardedBaseModOptions > = { getFilePath : ( config : ExportedConfigWithProps < ModType > , props : Props ) => Promise < string > | string ; read : ( filePath : string , config : ExportedConfigWithProps < ModType > , props : Props ) => Promise < ModType > | ModType ; write : ( filePath : string , config : ExportedConfigWithProps < ModType > , props : Props ) => Promise < void > | void ; /**
     * 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 ; } ; export type CreateBaseModProps < ModType , Props extends ForwardedBaseModOptions = ForwardedBaseModOptions > = { methodName : string ; platform : ModPlatform ; modName : string ; } & BaseModProviderMethods < ModType , Props > ; export function createBaseMod < ModType , Props extends ForwardedBaseModOptions = ForwardedBaseModOptions > ( { methodName , platform , modName , getFilePath , read , write , isIntrospective , } ) { const withUnknown = ( config , _props ) => { const props = _props || ( { } as Props ) ; return withBaseMod < ModType > ( config , { platform , mod : modName , skipEmptyMod : props . skipEmptyMod ?? true , saveToInternal : props . saveToInternal ?? false , isProvider : true , isIntrospective , async action ( { modRequest : { nextMod , ... modRequest } , ... config } ) { try { let results = { ... config , modRequest } ; const filePath = await getFilePath ( results , props ) ; debug ( `mods.${ platform }.${ modName }: file path: ${ filePath || '[skipped]' }` ) ; const modResults = await read ( filePath , results , props ) ; results = await nextMod ! ( { ... results , modResults , modRequest } ) ; assertModResults ( results , modRequest . platform , modRequest . modName ) ; await write ( filePath , results , props ) ; return results ; } catch ( error ) { error . message = `[${ platform }.${ modName }]: ${ methodName }: ${ error . message }` ; throw error ; } } } ) ; } ; if ( methodName ) { Object . defineProperty ( withUnknown , 'name' , { value : methodName } ) ; } return withUnknown ; } export function assertModResults ( results , platformName , modName ) { const ensuredResults = results ; if ( ! ensuredResults || typeof ensuredResults !== 'object' || ! ensuredResults ?. mods ) { throw new Error ( `Mod \`mods.${ platformName }.${ modName }\` evaluated to an object that is not a valid project config. Instead got: ${ JSON . stringify ( ensuredResults ) }` ) ; } return ensuredResults ; } function upperFirst ( name ) { return name . charAt ( 0 ) . toUpperCase ( ) + name . slice ( 1 ) ; } export function createPlatformBaseMod < ModType , Props extends ForwardedBaseModOptions = ForwardedBaseModOptions > ( { modName , ... props } ) { const methodName = `with${ upperFirst ( props . platform ) }${ upperFirst ( modName ) }BaseMod` ; return createBaseMod < ModType , Props > ( { methodName , modName , ... props } ) ; } /** A TS wrapper for creating provides */ export function provider < ModType , Props extends ForwardedBaseModOptions = ForwardedBaseModOptions > ( props ) { return props ; } /** Plugin to create and append base mods from file providers */ export function withGeneratedBaseMods < ModName extends string > ( config , { platform , providers , ... props } ) { return Object . entries ( providers ) . reduce ( ( config , [ modName , value ] ) => { const baseMod = createPlatformBaseMod ( { platform , modName , ... ( value as any ) } ) ; return baseMod ( config , props ) ; } , config ) ; }