forked from MetaMask/metamask-mobile
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfingerprint.config.js
More file actions
167 lines (151 loc) · 5.9 KB
/
Copy pathfingerprint.config.js
File metadata and controls
167 lines (151 loc) · 5.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/* eslint-disable import-x/no-commonjs */
/** @type {import('@expo/fingerprint').Config} */
/**
* This config is used when generating an EAS fingerprint for the project.
* Docs - https://docs.expo.dev/versions/latest/sdk/fingerprint/#fingerprintconfigjs
*/
// This config runs in a Node build environment (not in the React Native
// bundle), so importing a Node.js builtin is safe here. `child_process` is
// required to invoke the `expo-modules-autolinking` CLI synchronously — the
// config is loaded via CommonJS `require` with no top-level `await`, so the
// CLI's async programmatic API can't be used in its place.
// eslint-disable-next-line import-x/no-nodejs-modules
const { execSync } = require('child_process');
/**
* Returns the set of package names that have native code, as resolved by Expo
* autolinking. Falls back to `null` if autolinking fails (e.g. in environments
* where native tooling is unavailable), in which case callers should treat
* every package as native to stay safe.
*
* @returns {Set<string> | null}
*/
function getNativePackageNames() {
try {
const ios = JSON.parse(
execSync('yarn expo-modules-autolinking resolve -p ios --json', {
stdio: ['pipe', 'pipe', 'ignore'],
}).toString(),
);
const android = JSON.parse(
execSync('yarn expo-modules-autolinking resolve -p android --json', {
stdio: ['pipe', 'pipe', 'ignore'],
}).toString(),
);
return new Set([
...ios.modules.map((m) => m.packageName),
...android.modules.map((m) => m.packageName),
]);
} catch {
// Safe fallback: treat everything as native so nothing is accidentally
// excluded from the fingerprint.
return null;
}
}
const nativePackages = getNativePackageNames();
// Accumulates `package.json` content across streamed chunks (see fileHookTransform).
let packageJsonBuffer = '';
const config = {
/**
* Track files and directories under `extraSources` if they affect native code changes.
*
* Note: `.yarn/patches` is intentionally omitted here and handled instead via
* `fileHookTransform` below, where we filter it down to only patches that
* target native packages. This prevents JS-only patches from invalidating
* the fingerprint.
*/
extraSources: [
{
type: 'dir',
filePath: '.github/workflows',
reasons: ['Detect Github workflow changes.'],
},
{
type: 'dir',
filePath: '.github/scripts',
reasons: ['Detect Github workflow script changes.'],
},
{
type: 'file',
filePath: 'react-native.config.js',
reasons: ['Detect react-native.config.js changes.'],
},
{
type: 'file',
filePath: './scripts/build.sh',
reasons: ['Detect build configuration changes.'],
},
{
type: 'file',
filePath: './scripts/setup.mjs',
reasons: ['Detect setup script changes.'],
},
],
/**
* Transform sources before hashing to exclude JS-only changes from the
* fingerprint.
*
* - `package.json`: only the dependencies/devDependencies of native packages
* (as resolved by Expo autolinking) are included in the hash. Fields like
* `version`, `scripts`, and JS-only package version bumps are excluded.
*
* - `.yarn/patches/**`: patch files for JS-only packages are excluded. Only
* patches whose filename starts with a known native package name contribute
* to the hash.
*
* If autolinking resolution failed at startup (`nativePackages === null`),
* both transforms fall back to hashing everything unchanged.
*
* The hook is called as a streaming transform: once per file chunk (with
* `isEndOfFile === false`) and once more at end-of-file (`chunk === null`,
* `isEndOfFile === true`). Content larger than a single chunk (such as
* `package.json`) must therefore be buffered and parsed only once complete.
*
* @type {import('@expo/fingerprint').FileHookTransformFunction}
*/
fileHookTransform: (source, chunk, isEndOfFile) => {
// Fall back to hashing everything if we couldn't resolve native packages.
if (!nativePackages) return chunk;
// Filter package.json to only native-relevant dependency entries.
if (source.type === 'file' && source.filePath === 'package.json') {
// Buffer chunks until the full file has been read, then parse once.
if (chunk) packageJsonBuffer += chunk.toString();
if (!isEndOfFile) return '';
const pkg = JSON.parse(packageJsonBuffer);
packageJsonBuffer = '';
const filterToNative = (deps = {}) =>
Object.fromEntries(
Object.entries(deps).filter(([name]) => nativePackages.has(name)),
);
return JSON.stringify({
dependencies: filterToNative(pkg.dependencies),
devDependencies: filterToNative(pkg.devDependencies),
// Preserve autolinking config overrides (e.g. expo.autolinking).
expo: pkg.expo,
});
}
// Exclude patch files that only target JS-only packages.
if (
source.type === 'file' &&
source.filePath.startsWith('.yarn/patches/')
) {
const patchFilename = source.filePath.slice('.yarn/patches/'.length);
// Yarn patch filenames are formatted as `<package-name>-npm-<version>-<hash>.patch`.
// We extract the package name by finding the `-npm-` separator.
const npmSeparatorIndex = patchFilename.indexOf('-npm-');
const patchedPackage =
npmSeparatorIndex !== -1
? // Scoped packages use `@scope-name` in the filename (the `/` is replaced with `-`),
// so we restore it to match the package name from autolinking.
patchFilename
.slice(0, npmSeparatorIndex)
.replace(/^(@[^-]+)-/, '$1/')
: null;
if (!patchedPackage || !nativePackages.has(patchedPackage)) {
// Return empty string so this patch file contributes nothing to the hash.
return '';
}
}
return chunk;
},
};
module.exports = config;