New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@expo/fingerprint

Package Overview
Dependencies
Maintainers
0
Versions
91
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@expo/fingerprint - npm Package Compare versions

Comparing version 0.11.11 to 0.11.12-canary-20250303-4dba60e

45

build/Fingerprint.d.ts
import type { Fingerprint, FingerprintDiffItem, Options } from './Fingerprint.types';
/**
* Create a fingerprint from project
* Create a fingerprint for a project.
* @example
* ```js
* const fingerprint = await createFingerprintAsync('/app');
* console.log(fingerprint);
* ```
*/
export declare function createFingerprintAsync(projectRoot: string, options?: Options): Promise<Fingerprint>;
/**
* Create a native hash value from project
* Create a native hash value for a project.
*
* @example
* ```ts
* const hash = await createProjectHashAsync('/app');
* console.log(hash);
* ```
*/
export declare function createProjectHashAsync(projectRoot: string, options?: Options): Promise<string>;
/**
* Differentiate given `fingerprint` with the current project fingerprint state
* Diff the fingerprint with the fingerprint of the provided project.
*
* @example
* ```ts
* // 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);
* ```
*/
export declare function diffFingerprintChangesAsync(fingerprint: Fingerprint, projectRoot: string, options?: Options): Promise<FingerprintDiffItem[]>;
/**
* Differentiate two fingerprints with operation type.
* The implementation is assumed that the sources are sorted.
* Diff two fingerprints. The implementation assumes that the sources are sorted.
*
* @example
* ```ts
* // 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);
* ```
*/
export declare function diffFingerprints(fingerprint1: Fingerprint, fingerprint2: Fingerprint): FingerprintDiffItem[];

@@ -10,3 +10,8 @@ "use strict";

/**
* Create a fingerprint from project
* Create a fingerprint for a project.
* @example
* ```js
* const fingerprint = await createFingerprintAsync('/app');
* console.log(fingerprint);
* ```
*/

@@ -22,3 +27,9 @@ async function createFingerprintAsync(projectRoot, options) {

/**
* Create a native hash value from project
* Create a native hash value for a project.
*
* @example
* ```ts
* const hash = await createProjectHashAsync('/app');
* console.log(hash);
* ```
*/

@@ -31,3 +42,15 @@ async function createProjectHashAsync(projectRoot, options) {

/**
* Differentiate given `fingerprint` with the current project fingerprint state
* Diff the fingerprint with the fingerprint of the provided project.
*
* @example
* ```ts
* // 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);
* ```
*/

@@ -43,4 +66,16 @@ async function diffFingerprintChangesAsync(fingerprint, projectRoot, options) {

/**
* Differentiate two fingerprints with operation type.
* The implementation is assumed that the sources are sorted.
* Diff two fingerprints. The implementation assumes that the sources are sorted.
*
* @example
* ```ts
* // 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);
* ```
*/

@@ -47,0 +82,0 @@ function diffFingerprints(fingerprint1, fingerprint2) {

70

build/Fingerprint.types.d.ts

@@ -7,3 +7,3 @@ /// <reference types="node" />

* Hash value of the `source`.
* If the source is excluding by `Options.dirExcludes`, the value will be null.
* If the source is excluded the value will be null.
*/

@@ -19,7 +19,7 @@ hash: string | null;

/**
* Sources and their hash values to generate a fingerprint
* Sources and their hash values from which the project fingerprint was generated.
*/
sources: FingerprintSource[];
/**
* The final hash value of the whole fingerprint
* The final hash value of the whole project fingerprint.
*/

@@ -63,15 +63,18 @@ hash: string;

/**
* Only get native files from the given platforms. Default is `['android', 'ios']`.
* Limit native files to those for specified platforms.
* @default ['android', 'ios']
*/
platforms?: Platform[];
/**
* I/O concurrent limit. Default is the number of CPU core.
* I/O concurrency limit.
* @default The number of CPU cores.
*/
concurrentIoLimit?: number;
/**
* The algorithm passing to `crypto.createHash()`. Default is `'sha1'`.
* The algorithm to use for `crypto.createHash()`.
* @default 'sha1'
*/
hashAlgorithm?: string;
/**
* Excludes directories from hashing. This supported pattern is as `glob()`.
* Exclude specified directories from hashing. The supported pattern is the same as `glob()`.
* Default is `['android/build', 'android/app/build', 'android/app/.cxx', 'ios/Pods']`.

@@ -82,9 +85,9 @@ * @deprecated Use `ignorePaths` instead.

/**
* Ignore files and directories from hashing. This supported pattern is as `glob()`.
* Ignore files and directories from hashing. The supported pattern is the same as `glob()`.
*
* Please note that the pattern matching is slightly different from gitignore. For example, we don't support partial matching where `build` does not match `android/build`. You should use `'**' + '/build'` instead.
* @see [minimatch implementations](https://github.com/isaacs/minimatch#comparisons-to-other-fnmatchglob-implementations) for more reference.
* Please note that the pattern matching is slightly different from gitignore. Partial matching is unsupported. For example, `build` does not match `android/build`; instead, use `'**' + '/build'`.
* @see [minimatch implementations](https://github.com/isaacs/minimatch#comparisons-to-other-fnmatchglob-implementations) for further reference.
*
* Besides this `ignorePaths`, fingerprint comes with implicit default ignorePaths defined in `Options.DEFAULT_IGNORE_PATHS`.
* If you want to override the default ignorePaths, use `!` prefix.
* Fingerprint comes with implicit default ignorePaths defined in `Options.DEFAULT_IGNORE_PATHS`.
* If you want to override the default ignorePaths, use `!` prefix in `ignorePaths`.
*/

@@ -97,3 +100,3 @@ ignorePaths?: string[];

/**
* Skips some sources from fingerprint.
* Skips some sources from fingerprint. Value is the result of bitwise-OR'ing desired values of SourceSkips.
* @default DEFAULT_SOURCE_SKIPS

@@ -110,3 +113,3 @@ */

/**
* Use the react-native core autolinking sources from expo-modules-autolinking rather than @react-native-community/cli.
* Use the react-native core autolinking sources from `expo-modules-autolinking` rather than `@react-native-community/cli`.
* @default true for Expo SDK 52 and higher.

@@ -131,3 +134,3 @@ */

/**
* Supported options from fingerprint.config.js
* Supported options for use in fingerprint.config.js
*/

@@ -168,14 +171,2 @@ export type Config = Pick<Options, 'concurrentIoLimit' | 'hashAlgorithm' | 'ignorePaths' | 'extraSources' | 'enableReactImportsPatcher' | 'useRNCoreAutolinkingFromExpo' | 'debug' | 'fileHookTransform'> & {

};
export type NormalizedOptions = Omit<Options, 'ignorePaths'> & {
platforms: NonNullable<Options['platforms']>;
concurrentIoLimit: NonNullable<Options['concurrentIoLimit']>;
hashAlgorithm: NonNullable<Options['hashAlgorithm']>;
sourceSkips: NonNullable<Options['sourceSkips']>;
enableReactImportsPatcher: NonNullable<Options['enableReactImportsPatcher']>;
ignorePathMatchObjects: IMinimatch[];
/**
* A ignore pattern list specific for dir matching. It is built by `ignorePathMatchObjects` in runtime.
*/
ignoreDirMatchObjects: IMinimatch[];
};
export interface HashSourceFile {

@@ -185,3 +176,3 @@ type: 'file';

/**
* Reasons of this source coming from
* Reasons of this source coming from.
*/

@@ -194,3 +185,3 @@ reasons: string[];

/**
* Reasons of this source coming from
* Reasons of this source coming from.
*/

@@ -204,3 +195,3 @@ reasons: string[];

/**
* Reasons of this source coming from
* Reasons of this source coming from.
*/

@@ -213,3 +204,3 @@ reasons: string[];

hash: string;
/** Indicates whether the source is transformed by `fileHookTransform` */
/** Indicates whether the source is transformed by `fileHookTransform`. */
isTransformed?: boolean;

@@ -224,3 +215,3 @@ }

hash: string;
/** Indicates whether the source is transformed by `fileHookTransform` */
/** Indicates whether the source is transformed by `fileHookTransform`. */
isTransformed?: boolean;

@@ -248,2 +239,17 @@ }

export type HashResult = HashResultFile | HashResultDir | HashResultContents;
/**
* @hidden
*/
export type NormalizedOptions = Omit<Options, 'ignorePaths'> & {
platforms: NonNullable<Options['platforms']>;
concurrentIoLimit: NonNullable<Options['concurrentIoLimit']>;
hashAlgorithm: NonNullable<Options['hashAlgorithm']>;
sourceSkips: NonNullable<Options['sourceSkips']>;
enableReactImportsPatcher: NonNullable<Options['enableReactImportsPatcher']>;
ignorePathMatchObjects: IMinimatch[];
/**
* A ignore pattern list specific for dir matching. It is built by `ignorePathMatchObjects` in runtime.
*/
ignoreDirMatchObjects: IMinimatch[];
};
export {};

@@ -70,3 +70,3 @@ #!/usr/bin/env node

{bold Usage}
{dim $} npx @expo/fingeprint <command>
{dim $} npx @expo/fingerprint <command>

@@ -73,0 +73,0 @@ {bold Commands}

@@ -41,3 +41,7 @@ "use strict";

'--help': Boolean,
'--platform': String,
'--platform': [String],
'--concurrent-io-limit': Number,
'--hash-algorithm': String,
'--ignore-path': [String],
'--source-skips': Number,
'--debug': Boolean,

@@ -56,3 +60,7 @@ // Aliases

Options
--platform <string> Platform to generate a fingerprint for
--platform <string[]> Limit native files to those for specified platforms. Default is ['android', 'ios'].
--concurrent-io-limit <number> I/O concurrent limit. Default is the number of CPU cores.
--hash-algorithm <string> The algorithm to use for crypto.createHash(). Default is 'sha1'.
--ignore-path <string[]> Ignore files and directories from hashing. The supported pattern is the same as glob().
--source-skips <number> Skips some sources from fingerprint. Value is the result of bitwise-OR'ing desired values of SourceSkips. Default is DEFAULT_SOURCE_SKIPS.
--debug Whether to include verbose debug information in output

@@ -62,13 +70,43 @@ -h, --help Output usage information

}
const platform = args['--platform'];
if (platform && !['ios', 'android'].includes(platform)) {
throw new errors_1.CommandError(`Invalid platform argument: ${platform}`);
const platforms = args['--platform'];
if (platforms) {
if (!Array.isArray(platforms)) {
throw new errors_1.CommandError(`Invalid value for --platform`);
}
if (!platforms.every((elem) => ['ios', 'android'].includes(elem))) {
throw new errors_1.CommandError(`Invalid value for --platform: ${platforms}`);
}
}
const concurrentIoLimit = args['--concurrent-io-limit'];
if (concurrentIoLimit && !Number.isInteger(concurrentIoLimit)) {
throw new errors_1.CommandError(`Invalid value for --concurrent-io-limit argument: ${concurrentIoLimit}`);
}
const hashAlgorithm = args['--hash-algorithm'];
if (hashAlgorithm && typeof hashAlgorithm !== 'string') {
throw new errors_1.CommandError(`Invalid value for --hash-algorithm: ${hashAlgorithm}`);
}
const ignorePaths = args['--ignore-path'];
if (ignorePaths) {
if (!Array.isArray(ignorePaths)) {
throw new errors_1.CommandError(`Invalid value for --ignore-path`);
}
if (!ignorePaths.every((elem) => typeof elem === 'string')) {
throw new errors_1.CommandError(`Invalid value for --ignore-path: ${ignorePaths}`);
}
}
const sourceSkips = args['--source-skips'];
if (sourceSkips && !Number.isInteger(sourceSkips)) {
throw new errors_1.CommandError(`Invalid value for --source-skips argument: ${sourceSkips}`);
}
const options = {
debug: !!process.env.DEBUG || args['--debug'],
silent: true,
useRNCoreAutolinkingFromExpo: process.env['USE_RNCORE_AUTOLINKING_FROM_EXPO']
? (0, getenv_1.boolish)('USE_RNCORE_AUTOLINKING_FROM_EXPO')
: undefined,
...(platform ? { platforms: [platform] } : null),
silent: true,
...(platforms ? { platforms } : null),
...(concurrentIoLimit ? { concurrentIoLimit } : null),
...(hashAlgorithm ? { hashAlgorithm } : null),
...(ignorePaths ? { ignorePaths } : null),
...(sourceSkips ? { sourceSkips } : null),
};

@@ -75,0 +113,0 @@ const projectRoot = (0, args_1.getProjectRoot)(args);

{
"name": "@expo/fingerprint",
"version": "0.11.11",
"version": "0.11.12-canary-20250303-4dba60e",
"description": "A library to generate a fingerprint from a React Native project",

@@ -59,3 +59,3 @@ "main": "build/index.js",

"@types/find-up": "^4.0.0",
"expo-module-scripts": "^4.0.4",
"expo-module-scripts": "4.0.5-canary-20250303-4dba60e",
"glob": "^10.4.2",

@@ -65,3 +65,3 @@ "require-from-string": "^2.0.2",

},
"gitHead": "c01c449a1d6e6e8690bfcc88a778b46781a59674"
"gitHead": "4dba60e10b5d44b453073aa4968e9dbf312dea6c"
}

@@ -5,298 +5,9 @@ # @expo/fingerprint

## Table of Contents
# API documentation
- [API Usage](#api-usage)
- [CLI Usage](#cli-usage)
- [Customizations](#customizations)
- [**.fingerprintignore** file](#include-or-exclude-extra-files-to-ignored-paths-in-the-fingerprintignore-file)
- [**fingerprint.config.js** file](#fingerprintconfigjs)
- [Limitations](#limitations)
- [Documentation for the latest stable release](https://docs.expo.dev/versions/latest/sdk/fingerprint/)
- [Documentation for the main branch](https://docs.expo.dev/versions/unversioned/sdk/fingerprint/)
## API Usage
# Contributing
```ts
import * as Fingerprint from '@expo/fingerprint';
await Fingerprint.createFingerprintAsync('/projectRoot');
```
### createFingerprintAsync
Create a fingerprint from project
```ts
function createFingerprintAsync(projectRoot: string, options?: Options): Promise<Fingerprint>;
```
Example:
```ts
const fingerprint = await createFingerprintAsync('/app');
console.log(fingerprint);
```
```json
{
"sources": [
{
"type": "file",
"filePath": "app.json",
"reasons": ["expoConfig"],
"hash": "378083de0c6e6bb6caf8fb72df658b0b26fb29ef"
},
{
"type": "file",
"filePath": "eas.json",
"reasons": ["easBuild"],
"hash": "f723802b6ea916d1a6c4767b2299cc81ddb22eb4"
},
{
"type": "dir",
"filePath": "node_modules/expo",
"reasons": ["expoAutolinkingIos", "expoAutolinkingAndroid", "bareRncliAutolinking"],
"hash": "1faee4057fa943300905750b51c3b0cbf05f4b0d"
}
],
"hash": "bf8a3b08935f056270b1688333b02f1ef5fa25bf"
}
```
### createProjectHashAsync
Create a native hash value from project
```ts
function createProjectHashAsync(projectRoot: string, options?: Options): Promise<string>;
```
Example:
```ts
const hash = await createProjectHashAsync('/app');
console.log(hash);
```
```json
bf8a3b08935f056270b1688333b02f1ef5fa25bf
```
### diffFingerprintChangesAsync
Diff the given `fingerprint` with the current project fingerprint state
```ts
function diffFingerprintChangesAsync(
fingerprint: Fingerprint,
projectRoot: string,
options?: Options
): Promise<FingerprintSource[]>;
```
Example:
```ts
const fingerprint = {
sources: [
{
type: 'file',
filePath: 'app.json',
reasons: ['expoConfig'],
hash: '378083de0c6e6bb6caf8fb72df658b0b26fb29ef',
},
{
type: 'file',
filePath: 'eas.json',
reasons: ['easBuild'],
hash: 'f723802b6ea916d1a6c4767b2299cc81ddb22eb4',
},
{
type: 'dir',
filePath: 'node_modules/expo',
reasons: ['expoAutolinkingIos', 'expoAutolinkingAndroid', 'bareRncliAutolinking'],
hash: '1faee4057fa943300905750b51c3b0cbf05f4b0d',
},
],
hash: 'bf8a3b08935f056270b1688333b02f1ef5fa25bf',
};
const result = await diffFingerprintChangesAsync(fingerprint, '/app');
console.log(result);
```
```json
[
{
"op": "removed",
"source": {
"type": "file",
"filePath": "./assets/icon.png",
"reasons": ["expoConfigExternalFile"],
"hash": "3f71f5a8458c06b83424cc33e1f2481f601199ea"
}
},
{
"op": "added",
"source": {
"type": "dir",
"filePath": "ios",
"reasons": ["bareNativeDir"],
"hash": "2420400e6140a4ccfc350fc483b26efdfc26ddac"
}
},
{
"op": "changed",
"source": {
"type": "contents",
"id": "expoConfig",
"contents": "{\"ios\":{\"bundleIdentifier\":\"com.test\",\"supportsTablet\":true},\"name\":\"test\",\"platforms\":[\"ios\"],\"slug\":\"test\"}",
"reasons": ["expoConfig"],
"hash": "dd2a3ebb872b097f9c1e33780fb8db8688848fa0"
}
}
]
```
### diffFingerprints
Find the diff between two fingerprints
```ts
function diffFingerprints(
fingerprint1: Fingerprint,
fingerprint2: Fingerprint
): FingerprintSource[];
```
## CLI Usage
### Generate a fingerprint for a given project
`npx @expo/fingerprint /path/to/projectRoot`
### Generate a fingerprint for a given project and write it to a file
`npx @expo/fingerprint /path/to/projectRoot > fingerprint.json`
### Compare a fingerprint with the current project state
`npx @expo/fingerprint /path/to/projectRoot fingerprint.json`
## Customizations
### Include or exclude extra files to ignored paths in the **.fingerprintignore** file
Our default ignore paths, found here [`DEFAULT_IGNORE_PATHS`](https://github.com/expo/expo/blob/main/packages/%40expo/fingerprint/src/Options.ts#L11), make hashing fast and keep hashing results stable. If the default setup does not fit your workflow, you can add a **.fingerprintignore** file in your project root. It works like [**.gitignore**](https://git-scm.com/docs/gitignore#_pattern_format) but with some slight differences: We use `minimatch` for pattern matching with the [limitations](https://github.com/expo/expo/blob/9b9133c96f209b0616d1796aadae28913f8d012f/packages/%40expo/fingerprint/src/Fingerprint.types.ts#L46-L55).
Here's how to use **.fingerprintignore**: To skip a whole folder but keep some files, you can do this:
```
# Ignore the entire /app/ios folder
/app/ios/**/*
# But still keep /app/ios/Podfile and /app/ios/Podfile.lock
!/app/ios/Podfile
!/app/ios/Podfile.lock
```
### **fingerprint.config.js**
You can customize the fingerprinting behavior by creating a **fingerprint.config.js** file in your project root. This file allows you to specify custom configurations, such as skipping certain fingerprint sources, adding extra fingerprint sources, or enabling debug mode.
Below is an example **fingerprint.config.js** configuration, assuming you have `@expo/fingerprint` installed as a direct dependency:
```js
/** @type {import('@expo/fingerprint').Config} */
const config = {
sourceSkips: [
'ExpoConfigRuntimeVersionIfString',
'ExpoConfigVersions',
'PackageJsonAndroidAndIosScriptsIfNotContainRun',
],
};
module.exports = config;
```
If you are using `@expo/fingerprint` through `expo` (where `@expo/fingerprint` is installed as a transitive dependency), you can import fingerprint from `expo/fingerprint`:
```js
/** @type {import('expo/fingerprint').Config} */
const config = {
sourceSkips: [
'ExpoConfigRuntimeVersionIfString',
'ExpoConfigVersions',
'PackageJsonAndroidAndIosScriptsIfNotContainRun',
],
};
module.exports = config;
```
For supported configurations, you can refer to the [source code](https://github.com/expo/expo/blob/main/packages/%40expo/fingerprint/src/Config.ts#L38-L45) and [`SourceSkips.ts`](https://github.com/expo/expo/blob/main/packages/%40expo/fingerprint/src/sourcer/SourceSkips.ts) for supported `SourceSkips`.
## Limitations
### Limited support for [config-plugins raw functions](https://docs.expo.dev/config-plugins/plugins-and-mods/#raw-functions)
When using config-plugins with raw functions, it's essential to be aware of certain limitations, particularly in the context of fingerprinting. Expo makes its 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, Expo employs one of the following strategies to create serializable fingerprints for raw functions:
1. **Using `Function.name`**: Expo utilizes the `Function.name` property if available for named raw functions. This property provides a recognizable name for the function, which can be used as a fingerprint property.
2. **Using `withAnonymous`**: For anonymous raw functions without a `Function.name`, Expo resorts to using 'withAnonymous' as the fingerprint property. This is a generic identifier for anonymous functions.
Here's an example to illustrate these concepts:
```javascript
// In app.config.js
const { withInfoPlist } = require('expo/config-plugins');
const withMyPlugin = (config) => {
return withInfoPlist(config, (config) => {
config.modResults.NSLocationWhenInUseUsageDescription = 'Allow $(PRODUCT_NAME) to use your location';
return config;
});
};
export default ({ config }) => {
config.plugins ||= [];
config.plugins.push(withMyPlugin);
config.plugins.push((config) => config);
return config;
};`
```
In this example, Expo 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:
```javascript
// In ./plugins/withMyPlugin.js
const { withInfoPlist } = require('expo/config-plugins');
const withMyPlugin = (config) => {
return withInfoPlist(config, (config) => {
config.modResults.NSLocationWhenInUseUsageDescription =
'Allow $(PRODUCT_NAME) to use your location';
return config;
});
};
module.exports = withMyPlugin;
```
```json
// in app.json
{
"expo": {
// ...
"plugins": "./plugins/withMyPlugin"
}
}
```
By following these guidelines, you can effectively manage changes to config-plugins and ensure that fingerprinting remains consistent and reliable.
Contributions are very welcome! Please refer to guidelines described in the [contributing guide](https://github.com/expo/expo#contributing).

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc