Skip to content
This repository has been archived by the owner on Jan 18, 2024. It is now read-only.

Commit

Permalink
[config] Clarify README and add some documentation to withInterceptedMod
Browse files Browse the repository at this point in the history
  • Loading branch information
brentvatne committed Nov 10, 2020
1 parent 46db001 commit 44a9391
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 16 deletions.
22 changes: 11 additions & 11 deletions packages/config/src/plugins/README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Advanced configuration

Expo config is a powerful tool for generating native app code from a unified JavaScript interface. Most basic functionality can be controlled simply by using the the [static Expo config](https://docs.expo.io/versions/latest/config/app/), but some features require access to the native file system during ejection. To support complex behavior we've created config plugins, and mods (short for modifiers)!
@expo/config is a powerful tool for generating native app code from a unified JavaScript interface. Most basic functionality can be controlled by using the the [static Expo config](https://docs.expo.io/versions/latest/config/app/), but some features require manipulation of the native project files. To support complex behavior we've created config plugins, and mods (short for modifiers).

> Here is a [colorful chart](https://whimsical.com/UjytoYXT2RN43LywvWExfK) for visual learners.
## Plugins

A function which accepts a config, modifies it, then returns the modified config.

- Plugins should be named using a similar format: `with<Plugin Functionality>` i.e. `withFacebook`.
- Plugins should be synchronous and their return value should be evaluated. The only exception to this is when `mods` are added.
- Custom properties can be passed to the plugin using a second argument.
- Plugins should be named using the following convention: `with<Plugin Functionality>` i.e. `withFacebook`.
- Plugins should be synchronous and their return value should be serializable, except for any `mods` that are added.
- Optionally, a second argument can be passed to the plugin to configure it.

### Creating a Plugin

Expand Down Expand Up @@ -51,7 +51,7 @@ export default withMySDK(config, { apiKey: 'X-XXX-XXX' });

#### Chaining Plugins

If you have a lot of plugins, the code can start to get unreadable and hard to manipulate, to combat this, `expo/config-plugins` provides a plugin `withPlugins` which can be used to chain plugins together and execute them in order.
Once you add a few plugins, your `app.config.js` code can become difficult to read and manipulate. To combat this, `@expo/config-plugins` provides a `withPlugins` function which can be used to chain plugins together and execute them in order.

```js
/// Create a config
Expand All @@ -78,26 +78,26 @@ An async function which accepts a config and a data object, then manipulates and

Modifiers (mods for short) are added to the `mods` object of the Expo config. The `mods` object is different to the rest of the Expo config because it doesn't get serialized after the initial reading, this means you can use it to perform actions _during_ code generation. If possible, you should attempt to use basic plugins instead of mods as they're simpler to work with.

- `mods` are omitted in the manifest and cannot be accessed via `Constants.manifest`. `mods` exist for the sole purpose of modifying native files during code generation!
- mods are omitted from the manifest and cannot be accessed via `Constants.manifest`. mods exist for the sole purpose of modifying native files during code generation!
- mods can be used to read and write files safely during the `expo eject` command. This is how Expo CLI modifies the Info.plist, entitlements, xcproj, etc...
- mods are platform specific and should always be added to a platform specific object:

```js
{
mods: {
ios: { /* ... */ },
android: { /* ... */ }
ios: { /* ... */ },
android: { /* ... */ }
}
}
```

### How it works

- The config is read using `getConfig` from `expo/config`
- The config is read using `getConfig` from `@expo/config`
- All of the core functionality supported by Expo is added via plugins in `withExpoIOSPlugins`. This is stuff like name, version, icons, locales, etc.
- The config is passed to the compiler `compileModifiersAsync`
- The compiler adds base mods which are responsible for reading data (like Info.plist), executing a named mod (like `mods.ios.infoPlist`), then writing the results to the file system.
- The compiler iterates over all of the mods and asynchronously evaluates them while providing base props like the `projectRoot`.
- The compiler iterates over all of the mods and asynchronously evaluates them, providing some base props like the `projectRoot`.
- After each mod, error handling asserts if the mod chain was corrupted by an invalid mod.

### Default Modifiers
Expand Down Expand Up @@ -149,7 +149,7 @@ export default withCustomProductName(config, 'new_name');

### Experimental functionality

Some parts of the mod system aren't fully flushed out, these parts use the `withDangerousModifier` to read/write data without a base mod. These methods essentially act as their own base mod and cannot be extended. Icons for example currently use the dangerous mod to perform a single generation step with no ability to customize the results.
Some parts of the mod system aren't fully flushed out, these parts use `withDangerousModifier` to read/write data without a base mod. These methods essentially act as their own base mod and cannot be extended. Icons for example currently use the dangerous mod to perform a single generation step with no ability to customize the results.

```ts
export const withIcons: ConfigPlugin = config => {
Expand Down
20 changes: 15 additions & 5 deletions packages/config/src/plugins/core-plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ export function withExtendedMod<T>(
});
}

/**
* Plugin to intercept execution of a given `mod` with the given `action`.
* If an action was already set on the given `config` config for `mod`, then it
* will be provided to the `action` as `nextMod` when it's evaluated, otherwise
* `nextMod` will be an identity function.
*
* @param config exported config
* @param platform platform to target (ios or android)
* @param mod name of the platform function to intercept
* @param action method to run on the mod when the config is compiled
*/
export function withInterceptedMod<T>(
config: ExportedConfig,
{
Expand All @@ -75,15 +86,14 @@ export function withInterceptedMod<T>(
config.mods[platform] = {};
}

const modPlugin: Mod<T> =
const interceptedMod: Mod<T> =
(config.mods[platform] as Record<string, any>)[mod] ?? (config => config);

const extendedMod: Mod<T> = async ({ modRequest, ...config }) => {
// console.log(`-[mod]-> ${platform}.${mod}`);
return action({ ...config, modRequest: { ...modRequest, nextMod: modPlugin } });
const interceptingMod: Mod<T> = async ({ modRequest, ...config }) => {
return action({ ...config, modRequest: { ...modRequest, nextMod: interceptedMod } });
};

(config.mods[platform] as any)[mod] = extendedMod;
(config.mods[platform] as any)[mod] = interceptingMod;

return config;
}

0 comments on commit 44a9391

Please sign in to comment.