This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.
Expo Fingerprint
A library to generate a fingerprint from a React Native project.
@expo/fingerprint provides an API to generate a fingerprint (hash) of your project for use in determining compatibility between the native layer and JavaScript layer of your app. The hash calculation is configurable, but is by default derived from hashing app dependencies, custom native code, native project files, and configuration.
Installation
@expo/fingerprint is included with expo and expo-updates by default.
If you wish to use @expo/fingerprint as a standalone package, you can install it by running the command:
CLI Usage
Configuration
@expo/fingerprint provides defaults that should work for most projects, but also provides a few ways to configure the fingerprinting process to better fit your app structure and workflow.
.fingerprintignore
Placed in your project root, .fingerprintignore is a .gitignore-like ignore mechanism used to exclude files from hash calculation. All pattern paths are relative to the project root. It behaves similarly but instead uses minimatch for pattern matching which has some limitations (see documentation for ignorePaths under Options).
Here is an example .fingerprintignore configuration:
fingerprint.config.js
Placed in your project root, fingerprint.config.js allows you to specify custom hash calculation configuration beyond what is configurable in the .fingerprintignore. For supported configurations, see Config and SourceSkips.
Below is an example fingerprint.config.js configuration, assuming you have @expo/fingerprint installed as a direct dependency:
If you are using @expo/fingerprint through expo (where @expo/fingerprint is installed as a transitive dependency), you can import fingerprint from expo/fingerprint:
/** @type {import('expo/fingerprint').Config} */
Advanced: Customize sources before fingerprint hashing
In some cases, you may want to customize the sources before fingerprinting. For example:
- You want to remove sensitive data from the app config.
- You want to stabilize dynamic values in the app config.
- You want to transform file hashes to stable values.
To do this, you can use the fileHookTransform option in the fingerprint.config.js file to transform the sources before hashing. Learn more about the fileHookTransform option.
Limitations
Limited support for @expo/config-plugins raw functions
When using config plugins with raw functions, it's essential to be aware of certain limitations, particularly in the context of fingerprinting. The library makes a best effort to generate fingerprints for changes made through config plugins; however, raw functions pose specific challenges. Raw functions are not serializable as fingerprints, which means they cannot be directly used for generating unique hashes.
To work around this limitation, the library employs one of the following strategies to create serializable fingerprints for raw functions:
-
Using
Function.name: The library utilizes theFunction.nameproperty if available for named raw functions. This property provides a recognizable name for the function, which can be used as a fingerprint property. -
Using
withAnonymous: For anonymous raw functions without aFunction.name, the library resorts to usingwithAnonymousas the fingerprint property. This is a generic identifier for anonymous functions.
Here's an example to illustrate a case in which the library will use [withMyPlugin, withAnonymous] as plugin properties for fingerprint hashing:
It's important to note that due to this design, if you make changes to the implementation of raw config plugins functions, such as altering the Info.plist value within withMyPlugin, the fingerprint will still generate the same hash value. To ensure unique fingerprints when modifying config plugins implementations, consider the following options:
-
Avoid Anonymous Functions: Avoid using anonymous raw config plugins functions. Instead, use named functions whenever possible, and ensure that their names remain consistent as long as the implementation changes.
-
Use Local config plugins: Alternatively, you can create local config plugins as separate modules, each with its own export. This approach allows you to specify a different function name when making changes to the config plugins implementations.
Here's an example of using a local config plugin:
By following these guidelines, you can effectively manage changes to config plugins and ensure that fingerprinting remains consistent and reliable.
API
import * as Fingerprint from '@expo/fingerprint';
Constants
Methods
Create a fingerprint for a project.
Promise<Fingerprint>Example
const fingerprint = await createFingerprintAsync('/app'); console.log(fingerprint);
Create a native hash value for a project.
Promise<string>Example
const hash = await createProjectHashAsync('/app'); console.log(hash);
Diff the fingerprint with the fingerprint of the provided project.
Promise<FingerprintDiffItem[]>Example
// Create a fingerprint for the project const fingerprint = await createFingerprintAsync('/app'); // Make some changes to the project // Calculate the diff const diff = await diffFingerprintChangesAsync(fingerprint, '/app'); console.log(diff);
Diff two fingerprints. The implementation assumes that the sources are sorted.
FingerprintDiffItem[]Example
// Create a fingerprint for the project const fingerprint = await createFingerprintAsync('/app'); // Make some changes to the project // Create a fingerprint again const fingerprint2 = await createFingerprintAsync('/app'); const diff = await diffFingerprints(fingerprint, fingerprint2); console.log(diff);
Interfaces
Types
Supported options for use in fingerprint.config.js
Type: Pick<Options, 'concurrentIoLimit' | 'hashAlgorithm' | 'ignorePaths' | 'extraSources' | 'enableReactImportsPatcher' | 'useRNCoreAutolinkingFromExpo' | 'debug' | 'fileHookTransform'> extended by:
Literal type: union
Acceptable values are: DebugInfoFile | DebugInfoDir | DebugInfoContents
Hook function to transform file content sources before hashing.
Buffer | string | null
The source parameter for FileHookTransformFunction.
Type: object shaped as below:
Or object shaped as below:
Type: object shaped as below:
Or object shaped as below:
Or object shaped as below:
Literal type: union
Acceptable values are: HashResultFile | HashResultDir | HashResultContents
Literal type: union
Acceptable values are: HashSourceFile | HashSourceDir | HashSourceContents
Enums
Bitmask of values that can be used to skip certain parts of the sourcers when generating a fingerprint.
SourceSkips.ExpoConfigVersions = 1Versions in app.json, including Android versionCode and iOS buildNumber
SourceSkips.ExpoConfigRuntimeVersionIfString = 2runtimeVersion in app.json if it is a string
SourceSkips.ExpoConfigNames = 4App names in app.json, including shortName and description
SourceSkips.ExpoConfigIosBundleIdentifier = 16iOS bundle identifier in app.json
SourceSkips.ExpoConfigAssets = 128Assets in app.json, including icons and splash assets
SourceSkips.ExpoConfigAll = 256Skip the whole ExpoConfig. Prefer the other ExpoConfig source skips when possible and use this flag with caution. This will potentially ignore some native changes that should be part of most fingerprints. E.g., adding a new config plugin, changing the app icon, or changing the app name.
SourceSkips.PackageJsonAndroidAndIosScriptsIfNotContainRun = 512package.json scripts if android and ios items do not contain "run". Because prebuild will change the scripts in package.json, this is useful to generate a consistent fingerprint before and after prebuild.
SourceSkips.PackageJsonScriptsAll = 1024Skip the whole scripts section in the project's package.json.