Security News
JSR Working Group Kicks Off with Ambitious Roadmap and Plans for Open Governance
At its inaugural meeting, the JSR Working Group outlined plans for an open governance model and a roadmap to enhance JavaScript package management.
@expo/config-plugins
Advanced tools
The @expo/config-plugins package allows developers to customize and configure their Expo and React Native projects using JavaScript or TypeScript. This package provides an API for modifying the native app configuration files (like AndroidManifest.xml or Info.plist) programmatically, without having to eject from the managed Expo workflow. It enables the integration of native code and settings directly into an Expo managed project, making it easier to add custom functionality and configurations.
Modifying app.json / app.config.js
This feature allows developers to programmatically modify the app's configuration. In this example, the Android app is given camera permissions by modifying the AndroidManifest.xml through the app.json or app.config.js file.
const { withPlugins, AndroidConfig } = require('@expo/config-plugins');
module.exports = withPlugins(myExpoConfig, [
[AndroidConfig.Permissions.withPermissions, ['CAMERA']],
]);
Creating custom plugins
Developers can create custom config plugins to encapsulate and reuse configuration logic across multiple projects. This example demonstrates how to create a simple plugin that modifies the project configuration.
const { createRunOncePlugin, withPlugins } = require('@expo/config-plugins');
const withCustomPlugin = (config) => {
// Modify the config.
return config;
};
module.exports = createRunOncePlugin(withCustomPlugin, 'packageName', '1.0.0');
Integrating with native code
This feature enables direct modification of native project files, allowing for deeper integrations and customizations. The example shows how to use a dangerous mod to modify Android native files directly.
const { withDangerousMod } = require('@expo/config-plugins');
module.exports = function withCustomNativeCode(config) {
return withDangerousMod(config, [
'android',
async (config) => {
// Modify native Android files directly.
return config;
},
]);
};
Similar to @expo/config-plugins in that it allows for configuration of native app settings, react-native-config enables the management of environment variables for React Native apps. It differs in its approach by focusing on environment variables rather than direct modification of native app files.
While not directly similar to @expo/config-plugins, patch-package allows developers to apply patches to their node modules, which can include modifications to native code or configurations. It provides a different approach to solving the problem of customizing and fixing issues in dependencies.
The 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, 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 for visual learners.
A function which accepts a config, modifies it, then returns the modified config.
with<Plugin Functionality>
i.e. withFacebook
.mods
that are added.Here is an example of the most basic config plugin:
const withNothing = config => config;
Say you wanted to create a plugin which added custom values to the native iOS Info.plist:
const withMySDK = (config, { apiKey }) => {
// Ensure the objects exist
if (!config.ios) {
config.ios = {};
}
if (!config.ios.infoPlist) {
config.ios.infoPlist = {};
}
// Append the apiKey
config.ios.infoPlist['MY_CUSTOM_NATIVE_IOS_API_KEY'] = apiKey;
return config;
};
// 💡 Usage:
/// Create a config
const config = {
name: 'my app',
};
/// Use the plugin
export default withMySDK(config, { apiKey: 'X-XXX-XXX' });
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.
/// Create a config
const config = {
name: 'my app',
};
// ❌ Hard to read
withDelta(withFoo(withBar(config, 'input 1'), 'input 2'), 'input 3');
// ✅ Easy to read
import { withPlugins } from '@expo/config-plugins';
withPlugins(config, [
[withBar, 'input 1'],
[withFoo, 'input 2'],
[withDelta, 'input 3'],
]);
An async function which accepts a config and a data object, then manipulates and returns both as an object.
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.
Constants.manifest
. mods exist for the sole purpose of modifying native files during code generation!expo eject
command. This is how Expo CLI modifies the Info.plist, entitlements, xcproj, etc...{
mods: {
ios: { /* ... */ },
android: { /* ... */ }
}
}
getConfig
from @expo/config
withExpoIOSPlugins
. This is stuff like name, version, icons, locales, etc.compileModifiersAsync
mods.ios.infoPlist
), then writing the results to the file system.projectRoot
.
The following default mods are provided by the mod compiler for common file manipulation:
mods.ios.infoPlist
-- Modify the ios/<name>/Info.plist
as JSON (parsed with @expo/plist
).
mods.ios.entitlements
-- Modify the ios/<name>/<product-name>.entitlements
as JSON (parsed with @expo/plist
).
mods.ios.expoPlist
-- Modify the ios/<name>/Expo.plist
as JSON (Expo updates config for iOS) (parsed with @expo/plist
).
mods.ios.xcodeproj
-- Modify the ios/<name>.xcodeproj
as an XcodeProject
object (parsed with xcode
).
mods.android.manifest
-- Modify the android/app/src/main/AndroidManifest.xml
as JSON (parsed with xml2js
).
mods.android.strings
-- Modify the android/app/src/main/res/values/strings.xml
as JSON (parsed with xml2js
).
mods.android.mainActivity
-- Modify the android/app/src/main/<package>/MainActivity.java
as a string.
mods.android.appBuildGradle
-- Modify the android/app/build.gradle
as a string.
mods.android.projectBuildGradle
-- Modify the android/build.gradle
as a string.
After the mods are resolved, the contents of each mod will be written to disk. Custom default mods can be added to support new native files.
For example, you can create a mod to support the GoogleServices-Info.plist
, and pass it to other mods.
Say you wanted to write a mod to update the Xcode Project's "product name":
import { ConfigPlugin, withXcodeProject } from '@expo/config-plugins';
const withCustomProductName: ConfigPlugin = (config, customName) => {
return withXcodeProject(config, async config => {
// config = { modResults, modRequest, ...expoConfig }
const xcodeProject = config.modResults;
xcodeProject.productName = customName;
return config;
});
};
// 💡 Usage:
/// Create a config
const config = {
name: 'my app',
};
/// Use the plugin
export default withCustomProductName(config, 'new_name');
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.
export const withIcons: ConfigPlugin = config => {
return withDangerousModifier(config, async config => {
// No modifications are made to the config
await setIconsAsync(config, config.modRequest.projectRoot);
return config;
});
};
Be careful using withDangerousModifier
as it is subject to change in the future.
The order with which it gets executed is not reliable either.
Currently dangerous mods run first before all other modifiers, this is because we use dangerous mods internally for large file system refactoring like when the package name changes.
FAQs
A library for Expo config plugins
The npm package @expo/config-plugins receives a total of 1,076,279 weekly downloads. As such, @expo/config-plugins popularity was classified as popular.
We found that @expo/config-plugins demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 27 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
At its inaugural meeting, the JSR Working Group outlined plans for an open governance model and a roadmap to enhance JavaScript package management.
Security News
Research
An advanced npm supply chain attack is leveraging Ethereum smart contracts for decentralized, persistent malware control, evading traditional defenses.
Security News
Research
Attackers are impersonating Sindre Sorhus on npm with a fake 'chalk-node' package containing a malicious backdoor to compromise developers' projects.