@expo/fingerprint 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +70 -0
- package/__mocks__/@expo/spawn-async.ts +30 -0
- package/__mocks__/fs/promises.ts +15 -0
- package/build/Options.js +24 -0
- package/build/Options.js.map +1 -1
- package/build/hash/Hash.d.ts +0 -5
- package/build/hash/Hash.js +4 -22
- package/build/hash/Hash.js.map +1 -1
- package/build/sourcer/Expo.js +29 -34
- package/build/sourcer/Expo.js.map +1 -1
- package/build/sourcer/ExpoConfigLoader.d.ts +7 -0
- package/build/sourcer/ExpoConfigLoader.js +80 -0
- package/build/sourcer/ExpoConfigLoader.js.map +1 -0
- package/build/utils/Path.d.ts +5 -0
- package/build/utils/Path.js +26 -0
- package/build/utils/Path.js.map +1 -0
- package/e2e/__tests__/bare-test.ts +7 -0
- package/e2e/__tests__/managed-test.ts +7 -0
- package/package.json +3 -3
- package/src/Options.ts +25 -0
- package/src/hash/Hash.ts +1 -26
- package/src/hash/__tests__/Hash-test.ts +0 -36
- package/src/sourcer/Expo.ts +34 -40
- package/src/sourcer/ExpoConfigLoader.ts +84 -0
- package/src/sourcer/__tests__/Expo-test.ts +24 -100
- package/src/sourcer/__tests__/PatchPackage-test.ts +3 -0
- package/src/sourcer/__tests__/Sourcer-test.ts +7 -0
- package/src/utils/Path.ts +26 -0
- package/src/utils/__tests__/Path-test.ts +36 -0
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,22 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 0.4.0 — 2023-10-17
|
|
14
|
+
|
|
15
|
+
### 💡 Others
|
|
16
|
+
|
|
17
|
+
- Transpile for Node 18 (LTS). ([#24471](https://github.com/expo/expo/pull/24471) by [@EvanBacon](https://github.com/EvanBacon))
|
|
18
|
+
|
|
19
|
+
## 0.3.0 — 2023-09-20
|
|
20
|
+
|
|
21
|
+
### 🛠 Breaking changes
|
|
22
|
+
|
|
23
|
+
- This version includes fingerprint result breaking changes. ([#24520](https://github.com/expo/expo/pull/24520) by [@kudo](https://github.com/kudo))
|
|
24
|
+
|
|
25
|
+
### 🎉 New features
|
|
26
|
+
|
|
27
|
+
- Improve fingerprint sourcing scope for local config-plugins. ([#24520](https://github.com/expo/expo/pull/24520) by [@kudo](https://github.com/kudo))
|
|
28
|
+
|
|
13
29
|
## 0.2.0 — 2023-09-08
|
|
14
30
|
|
|
15
31
|
### 🛠 Breaking changes
|
package/README.md
CHANGED
|
@@ -116,3 +116,73 @@ console.log(result);
|
|
|
116
116
|
## CLI Usage
|
|
117
117
|
|
|
118
118
|
`npx @expo/fingerprint /path/to/projectRoot`
|
|
119
|
+
|
|
120
|
+
## Limitations
|
|
121
|
+
|
|
122
|
+
## Limited support for [config-plugins raw functions](https://docs.expo.dev/config-plugins/plugins-and-mods/#raw-functions)
|
|
123
|
+
|
|
124
|
+
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.
|
|
125
|
+
|
|
126
|
+
To work around this limitation, Expo employs one of the following strategies to create serializable fingerprints for raw functions:
|
|
127
|
+
|
|
128
|
+
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.
|
|
129
|
+
|
|
130
|
+
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.
|
|
131
|
+
|
|
132
|
+
Here's an example to illustrate these concepts:
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
// In app.config.js
|
|
136
|
+
const { withInfoPlist } = require('expo/config-plugins');
|
|
137
|
+
|
|
138
|
+
const withMyPlugin = (config) => {
|
|
139
|
+
return withInfoPlist(config, (config) => {
|
|
140
|
+
config.modResults.NSLocationWhenInUseUsageDescription = 'Allow $(PRODUCT_NAME) to use your location';
|
|
141
|
+
return config;
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export default ({ config }) => {
|
|
146
|
+
config.plugins ||= [];
|
|
147
|
+
config.plugins.push(withMyPlugin);
|
|
148
|
+
config.plugins.push((config) => config);
|
|
149
|
+
return config;
|
|
150
|
+
};`
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
In this example, Expo will use ['withMyPlugin', 'withAnonymous'] as plugin properties for fingerprint hashing.
|
|
154
|
+
|
|
155
|
+
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:
|
|
156
|
+
|
|
157
|
+
- **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.
|
|
158
|
+
|
|
159
|
+
- **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.
|
|
160
|
+
|
|
161
|
+
Here's an example of using a local config-plugin:
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
// In ./plugins/withMyPlugin.js
|
|
165
|
+
const { withInfoPlist } = require('expo/config-plugins');
|
|
166
|
+
|
|
167
|
+
const withMyPlugin = (config) => {
|
|
168
|
+
return withInfoPlist(config, (config) => {
|
|
169
|
+
config.modResults.NSLocationWhenInUseUsageDescription =
|
|
170
|
+
'Allow $(PRODUCT_NAME) to use your location';
|
|
171
|
+
return config;
|
|
172
|
+
});
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
module.exports = withMyPlugin;
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
// in app.json
|
|
180
|
+
{
|
|
181
|
+
"expo": {
|
|
182
|
+
// ...
|
|
183
|
+
"plugins": "./plugins/withMyPlugin"
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
By following these guidelines, you can effectively manage changes to config-plugins and ensure that fingerprinting remains consistent and reliable.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import spawnAsync from '@expo/spawn-async';
|
|
2
|
+
import { getConfig } from 'expo/config';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
type SpawnAsyncType = typeof spawnAsync;
|
|
6
|
+
|
|
7
|
+
const origSpawnAsync = jest.requireActual('@expo/spawn-async') as SpawnAsyncType;
|
|
8
|
+
const mockSpawnAsync = jest
|
|
9
|
+
.fn()
|
|
10
|
+
.mockImplementation(
|
|
11
|
+
async (
|
|
12
|
+
command: Parameters<SpawnAsyncType>[0],
|
|
13
|
+
args: Parameters<SpawnAsyncType>[1],
|
|
14
|
+
options: Parameters<SpawnAsyncType>[2]
|
|
15
|
+
) => {
|
|
16
|
+
if (args != null && args.length >= 2 && path.parse(args[0]).name === 'ExpoConfigLoader') {
|
|
17
|
+
// For unit tests, we don't really spawn a process to execute the ExpoConfigLoader because the file system is just a memfs.
|
|
18
|
+
// Rather than that, we just call `getConfig` directly.
|
|
19
|
+
const projectRoot = args[1];
|
|
20
|
+
const config = await getConfig(projectRoot, { skipSDKVersionRequirement: true });
|
|
21
|
+
const stdout = JSON.stringify({ config, loadedModules: [] });
|
|
22
|
+
return {
|
|
23
|
+
stdout,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return origSpawnAsync(command, args, options);
|
|
27
|
+
}
|
|
28
|
+
) as SpawnAsyncType;
|
|
29
|
+
|
|
30
|
+
module.exports = mockSpawnAsync;
|
package/__mocks__/fs/promises.ts
CHANGED
|
@@ -1,2 +1,17 @@
|
|
|
1
1
|
import { fs } from 'memfs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Further mock `fs.promises` to ensure the parent directory exists.
|
|
6
|
+
* We used to call `fs.promises.mkdtemp(path.join(os.tmpdir(), ...))`, so that we don't have to worry about os.tmpdir() exists.
|
|
7
|
+
*/
|
|
8
|
+
const origMkdtemp = fs.promises.mkdtemp;
|
|
9
|
+
fs.promises.mkdtemp = (
|
|
10
|
+
prefix: string,
|
|
11
|
+
options?: Parameters<typeof origMkdtemp>[1]
|
|
12
|
+
): ReturnType<typeof origMkdtemp> => {
|
|
13
|
+
fs.mkdirSync(path.dirname(prefix), { recursive: true });
|
|
14
|
+
return origMkdtemp(prefix, options);
|
|
15
|
+
};
|
|
16
|
+
|
|
2
17
|
module.exports = fs.promises;
|
package/build/Options.js
CHANGED
|
@@ -19,6 +19,30 @@ exports.DEFAULT_IGNORE_PATHS = [
|
|
|
19
19
|
'app.config.js',
|
|
20
20
|
'app.config.json',
|
|
21
21
|
'app.json',
|
|
22
|
+
// Ignore default javascript files when calling `getConfig()`
|
|
23
|
+
'**/node_modules/@babel/**/*',
|
|
24
|
+
'**/node_modules/@expo/**/*',
|
|
25
|
+
'**/node_modules/@jridgewell/**/*',
|
|
26
|
+
'**/node_modules/expo/config.js',
|
|
27
|
+
'**/node_modules/expo/config-plugins.js',
|
|
28
|
+
`**/node_modules/{${[
|
|
29
|
+
'debug',
|
|
30
|
+
'escape-string-regexp',
|
|
31
|
+
'getenv',
|
|
32
|
+
'graceful-fs',
|
|
33
|
+
'has-flag',
|
|
34
|
+
'imurmurhash',
|
|
35
|
+
'js-tokens',
|
|
36
|
+
'json5',
|
|
37
|
+
'lines-and-columns',
|
|
38
|
+
'require-from-string',
|
|
39
|
+
'resolve-from',
|
|
40
|
+
'signal-exit',
|
|
41
|
+
'sucrase',
|
|
42
|
+
'supports-color',
|
|
43
|
+
'ts-interface-checker',
|
|
44
|
+
'write-file-atomic',
|
|
45
|
+
].join(',')}}/**/*`,
|
|
22
46
|
];
|
|
23
47
|
async function normalizeOptionsAsync(projectRoot, options) {
|
|
24
48
|
return {
|
package/build/Options.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Options.js","sourceRoot":"","sources":["../src/Options.ts"],"names":[],"mappings":";;;;;;AAAA,2DAA6B;AAC7B,4CAAoB;AACpB,gDAAwB;AAIX,QAAA,2BAA2B,GAAG,oBAAoB,CAAC;AAEnD,QAAA,oBAAoB,GAAG;IAClC,mCAA2B;IAC3B,uBAAuB;IACvB,2BAA2B;IAC3B,0BAA0B;IAC1B,kBAAkB;IAElB,2FAA2F;IAC3F,eAAe;IACf,eAAe;IACf,iBAAiB;IACjB,UAAU;
|
|
1
|
+
{"version":3,"file":"Options.js","sourceRoot":"","sources":["../src/Options.ts"],"names":[],"mappings":";;;;;;AAAA,2DAA6B;AAC7B,4CAAoB;AACpB,gDAAwB;AAIX,QAAA,2BAA2B,GAAG,oBAAoB,CAAC;AAEnD,QAAA,oBAAoB,GAAG;IAClC,mCAA2B;IAC3B,uBAAuB;IACvB,2BAA2B;IAC3B,0BAA0B;IAC1B,kBAAkB;IAElB,2FAA2F;IAC3F,eAAe;IACf,eAAe;IACf,iBAAiB;IACjB,UAAU;IAEV,6DAA6D;IAC7D,6BAA6B;IAC7B,4BAA4B;IAC5B,kCAAkC;IAClC,gCAAgC;IAChC,wCAAwC;IACxC,oBAAoB;QAClB,OAAO;QACP,sBAAsB;QACtB,QAAQ;QACR,aAAa;QACb,UAAU;QACV,aAAa;QACb,WAAW;QACX,OAAO;QACP,mBAAmB;QACnB,qBAAqB;QACrB,cAAc;QACd,aAAa;QACb,SAAS;QACT,gBAAgB;QAChB,sBAAsB;QACtB,mBAAmB;KACpB,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ;CACpB,CAAC;AAEK,KAAK,UAAU,qBAAqB,CACzC,WAAmB,EACnB,OAAiB;IAEjB,OAAO;QACL,GAAG,OAAO;QACV,SAAS,EAAE,OAAO,EAAE,SAAS,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC;QACnD,iBAAiB,EAAE,OAAO,EAAE,iBAAiB,IAAI,YAAE,CAAC,IAAI,EAAE,CAAC,MAAM;QACjE,aAAa,EAAE,OAAO,EAAE,aAAa,IAAI,MAAM;QAC/C,WAAW,EAAE,MAAM,uBAAuB,CAAC,WAAW,EAAE,OAAO,CAAC;KACjE,CAAC;AACJ,CAAC;AAXD,sDAWC;AAED,KAAK,UAAU,uBAAuB,CAAC,WAAmB,EAAE,OAAiB;IAC3E,MAAM,WAAW,GAAG;QAClB,GAAG,4BAAoB;QACvB,GAAG,CAAC,OAAO,EAAE,WAAW,IAAI,EAAE,CAAC;QAC/B,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,GAAG,UAAU,OAAO,CAAC,IAAI,EAAE,CAAC;KAC3E,CAAC;IAEF,MAAM,qBAAqB,GAAG,cAAI,CAAC,IAAI,CAAC,WAAW,EAAE,mCAA2B,CAAC,CAAC;IAClF,IAAI;QACF,MAAM,iBAAiB,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;QAC3E,MAAM,sBAAsB,GAAG,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7D,KAAK,MAAM,IAAI,IAAI,sBAAsB,EAAE;YACzC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAChC,IAAI,WAAW,EAAE;gBACf,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;aAC/B;SACF;KACF;IAAC,MAAM,GAAE;IAEV,OAAO,WAAW,CAAC;AACrB,CAAC"}
|
package/build/hash/Hash.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import minimatch from 'minimatch';
|
|
2
1
|
import pLimit from 'p-limit';
|
|
3
2
|
import type { Fingerprint, FingerprintSource, HashResult, HashSource, HashSourceContents, NormalizedOptions } from '../Fingerprint.types';
|
|
4
3
|
/**
|
|
@@ -14,10 +13,6 @@ export declare function createFingerprintSourceAsync(source: HashSource, limiter
|
|
|
14
13
|
* Create a `HashResult` from a file
|
|
15
14
|
*/
|
|
16
15
|
export declare function createFileHashResultsAsync(filePath: string, limiter: pLimit.Limit, projectRoot: string, options: NormalizedOptions): Promise<HashResult | null>;
|
|
17
|
-
/**
|
|
18
|
-
* Indicate the given `filePath` should be excluded by `ignorePaths`
|
|
19
|
-
*/
|
|
20
|
-
export declare function isIgnoredPath(filePath: string, ignorePaths: string[], minimatchOptions?: minimatch.IOptions): boolean;
|
|
21
16
|
/**
|
|
22
17
|
* Create `HashResult` for a dir.
|
|
23
18
|
* If the dir is excluded, returns null rather than a HashResult
|
package/build/hash/Hash.js
CHANGED
|
@@ -3,13 +3,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.createSourceId = exports.createContentsHashResultsAsync = exports.createDirHashResultsAsync = exports.
|
|
6
|
+
exports.createSourceId = exports.createContentsHashResultsAsync = exports.createDirHashResultsAsync = exports.createFileHashResultsAsync = exports.createFingerprintSourceAsync = exports.createFingerprintFromSourcesAsync = void 0;
|
|
7
7
|
const crypto_1 = require("crypto");
|
|
8
8
|
const fs_1 = require("fs");
|
|
9
9
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
10
|
-
const minimatch_1 = __importDefault(require("minimatch"));
|
|
11
10
|
const p_limit_1 = __importDefault(require("p-limit"));
|
|
12
11
|
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const Path_1 = require("../utils/Path");
|
|
13
13
|
const Profile_1 = require("../utils/Profile");
|
|
14
14
|
/**
|
|
15
15
|
* Create a `Fingerprint` from `HashSources` array
|
|
@@ -80,7 +80,7 @@ async function createFileHashResultsAsync(filePath, limiter, projectRoot, option
|
|
|
80
80
|
*/
|
|
81
81
|
return limiter(() => {
|
|
82
82
|
return new Promise((resolve, reject) => {
|
|
83
|
-
if (isIgnoredPath(filePath, options.ignorePaths)) {
|
|
83
|
+
if ((0, Path_1.isIgnoredPath)(filePath, options.ignorePaths)) {
|
|
84
84
|
return resolve(null);
|
|
85
85
|
}
|
|
86
86
|
let resolved = false;
|
|
@@ -103,30 +103,12 @@ async function createFileHashResultsAsync(filePath, limiter, projectRoot, option
|
|
|
103
103
|
});
|
|
104
104
|
}
|
|
105
105
|
exports.createFileHashResultsAsync = createFileHashResultsAsync;
|
|
106
|
-
/**
|
|
107
|
-
* Indicate the given `filePath` should be excluded by `ignorePaths`
|
|
108
|
-
*/
|
|
109
|
-
function isIgnoredPath(filePath, ignorePaths, minimatchOptions = { dot: true }) {
|
|
110
|
-
const minimatchObjs = ignorePaths.map((ignorePath) => new minimatch_1.default.Minimatch(ignorePath, minimatchOptions));
|
|
111
|
-
let result = false;
|
|
112
|
-
for (const minimatchObj of minimatchObjs) {
|
|
113
|
-
const currMatch = minimatchObj.match(filePath);
|
|
114
|
-
if (minimatchObj.negate && result && !currMatch) {
|
|
115
|
-
// Special handler for negate (!pattern).
|
|
116
|
-
// As long as previous match result is true and not matched from the current negate pattern, we should early return.
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
result || (result = currMatch);
|
|
120
|
-
}
|
|
121
|
-
return result;
|
|
122
|
-
}
|
|
123
|
-
exports.isIgnoredPath = isIgnoredPath;
|
|
124
106
|
/**
|
|
125
107
|
* Create `HashResult` for a dir.
|
|
126
108
|
* If the dir is excluded, returns null rather than a HashResult
|
|
127
109
|
*/
|
|
128
110
|
async function createDirHashResultsAsync(dirPath, limiter, projectRoot, options, depth = 0) {
|
|
129
|
-
if (isIgnoredPath(dirPath, options.ignorePaths)) {
|
|
111
|
+
if ((0, Path_1.isIgnoredPath)(dirPath, options.ignorePaths)) {
|
|
130
112
|
return null;
|
|
131
113
|
}
|
|
132
114
|
const dirents = (await promises_1.default.readdir(path_1.default.join(projectRoot, dirPath), { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
|
package/build/hash/Hash.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Hash.js","sourceRoot":"","sources":["../../src/hash/Hash.ts"],"names":[],"mappings":";;;;;;AAAA,mCAAoC;AACpC,2BAAsC;AACtC,2DAA6B;AAC7B,
|
|
1
|
+
{"version":3,"file":"Hash.js","sourceRoot":"","sources":["../../src/hash/Hash.ts"],"names":[],"mappings":"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"}
|
package/build/sourcer/Expo.js
CHANGED
|
@@ -5,19 +5,27 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.sortExpoAutolinkingAndroidConfig = exports.getExpoAutolinkingIosSourcesAsync = exports.getExpoAutolinkingAndroidSourcesAsync = exports.getEasBuildSourcesAsync = exports.getExpoConfigSourcesAsync = void 0;
|
|
7
7
|
const spawn_async_1 = __importDefault(require("@expo/spawn-async"));
|
|
8
|
-
const assert_1 = __importDefault(require("assert"));
|
|
9
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
-
const
|
|
9
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
10
|
+
const os_1 = __importDefault(require("os"));
|
|
11
11
|
const path_1 = __importDefault(require("path"));
|
|
12
12
|
const resolve_from_1 = __importDefault(require("resolve-from"));
|
|
13
|
+
const ExpoConfigLoader_1 = require("./ExpoConfigLoader");
|
|
13
14
|
const Utils_1 = require("./Utils");
|
|
14
15
|
const debug = require('debug')('expo:fingerprint:sourcer:Expo');
|
|
15
16
|
async function getExpoConfigSourcesAsync(projectRoot, options) {
|
|
17
|
+
if (!resolve_from_1.default.silent(path_1.default.resolve(projectRoot), 'expo/config')) {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
16
20
|
const results = [];
|
|
17
21
|
let config;
|
|
22
|
+
let loadedModules = [];
|
|
23
|
+
const ignoredFile = await createTempIgnoredFileAsync(options);
|
|
18
24
|
try {
|
|
19
|
-
const {
|
|
20
|
-
|
|
25
|
+
const { stdout } = await (0, spawn_async_1.default)('node', [(0, ExpoConfigLoader_1.getExpoConfigLoaderPath)(), path_1.default.resolve(projectRoot), ignoredFile], { cwd: __dirname });
|
|
26
|
+
const stdoutJson = JSON.parse(stdout);
|
|
27
|
+
config = stdoutJson.config;
|
|
28
|
+
loadedModules = stdoutJson.loadedModules;
|
|
21
29
|
results.push({
|
|
22
30
|
type: 'contents',
|
|
23
31
|
id: 'expoConfig',
|
|
@@ -26,7 +34,9 @@ async function getExpoConfigSourcesAsync(projectRoot, options) {
|
|
|
26
34
|
});
|
|
27
35
|
}
|
|
28
36
|
catch (e) {
|
|
29
|
-
|
|
37
|
+
if (e instanceof Error) {
|
|
38
|
+
console.warn(`Cannot get Expo config from an Expo project - ${e.message}: `, e.stack);
|
|
39
|
+
}
|
|
30
40
|
return [];
|
|
31
41
|
}
|
|
32
42
|
// external files in config
|
|
@@ -63,17 +73,15 @@ async function getExpoConfigSourcesAsync(projectRoot, options) {
|
|
|
63
73
|
}))).filter(Boolean);
|
|
64
74
|
results.push(...externalFileSources);
|
|
65
75
|
// config plugins
|
|
66
|
-
const
|
|
67
|
-
|
|
76
|
+
const configPluginModules = loadedModules.map((modulePath) => ({
|
|
77
|
+
type: 'file',
|
|
78
|
+
filePath: modulePath,
|
|
79
|
+
reasons: ['expoConfigPlugins'],
|
|
80
|
+
}));
|
|
81
|
+
results.push(...configPluginModules);
|
|
68
82
|
return results;
|
|
69
83
|
}
|
|
70
84
|
exports.getExpoConfigSourcesAsync = getExpoConfigSourcesAsync;
|
|
71
|
-
function findUpPluginRoot(entryFile) {
|
|
72
|
-
const entryRoot = path_1.default.dirname(entryFile);
|
|
73
|
-
const packageJson = find_up_1.default.sync('package.json', { cwd: path_1.default.dirname(entryFile) });
|
|
74
|
-
(0, assert_1.default)(packageJson, `No package.json found for module "${entryRoot}"`);
|
|
75
|
-
return path_1.default.dirname(packageJson);
|
|
76
|
-
}
|
|
77
85
|
function normalizeExpoConfig(config) {
|
|
78
86
|
// Deep clone by JSON.parse/stringify that assumes the config is serializable.
|
|
79
87
|
const normalizedConfig = JSON.parse(JSON.stringify(config));
|
|
@@ -81,27 +89,14 @@ function normalizeExpoConfig(config) {
|
|
|
81
89
|
delete normalizedConfig._internal;
|
|
82
90
|
return (0, Utils_1.stringifyJsonSorted)(normalizedConfig);
|
|
83
91
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const pluginPackageEntryFile = resolve_from_1.default.silent(projectRoot, pluginPackageName);
|
|
93
|
-
const pluginPackageRoot = pluginPackageEntryFile
|
|
94
|
-
? findUpPluginRoot(pluginPackageEntryFile)
|
|
95
|
-
: null;
|
|
96
|
-
if (pluginPackageRoot) {
|
|
97
|
-
debug(`Adding config-plugin root - ${chalk_1.default.dim(pluginPackageRoot)}`);
|
|
98
|
-
return { type: 'dir', filePath: path_1.default.relative(projectRoot, pluginPackageRoot), reasons };
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return null;
|
|
102
|
-
});
|
|
103
|
-
const results = nullableResults.filter(Boolean);
|
|
104
|
-
return results;
|
|
92
|
+
/**
|
|
93
|
+
* Create a temporary file with ignored paths from options that will be read by the ExpoConfigLoader.
|
|
94
|
+
*/
|
|
95
|
+
async function createTempIgnoredFileAsync(options) {
|
|
96
|
+
await promises_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'expo-fingerprint-'));
|
|
97
|
+
const ignoredFile = path_1.default.join(os_1.default.tmpdir(), '.fingerprintignore');
|
|
98
|
+
await promises_1.default.writeFile(ignoredFile, options.ignorePaths.join('\n'));
|
|
99
|
+
return ignoredFile;
|
|
105
100
|
}
|
|
106
101
|
async function getEasBuildSourcesAsync(projectRoot, options) {
|
|
107
102
|
const files = ['eas.json', '.easignore'];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Expo.js","sourceRoot":"","sources":["../../src/sourcer/Expo.ts"],"names":[],"mappings":";;;;;;AAAA,oEAA2C;AAC3C,
|
|
1
|
+
{"version":3,"file":"Expo.js","sourceRoot":"","sources":["../../src/sourcer/Expo.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* A helper script to load the Expo config and loaded plugins from a project
|
|
4
|
+
*/
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.getExpoConfigLoaderPath = void 0;
|
|
10
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
11
|
+
const module_1 = __importDefault(require("module"));
|
|
12
|
+
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const resolve_from_1 = __importDefault(require("resolve-from"));
|
|
14
|
+
const Options_1 = require("../Options");
|
|
15
|
+
const Path_1 = require("../utils/Path");
|
|
16
|
+
async function runAsync(programName, args = []) {
|
|
17
|
+
if (args.length < 1) {
|
|
18
|
+
console.log(`Usage: ${programName} <projectRoot> [ignoredFile]`);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const projectRoot = path_1.default.resolve(args[0]);
|
|
22
|
+
const ignoredFile = args[1] ? path_1.default.resolve(args[1]) : null;
|
|
23
|
+
// @ts-expect-error: module internal _cache
|
|
24
|
+
const loadedModulesBefore = new Set(Object.keys(module_1.default._cache));
|
|
25
|
+
const { getConfig } = require((0, resolve_from_1.default)(path_1.default.resolve(projectRoot), 'expo/config'));
|
|
26
|
+
const config = await getConfig(projectRoot, { skipSDKVersionRequirement: true });
|
|
27
|
+
// @ts-expect-error: module internal _cache
|
|
28
|
+
const loadedModules = Object.keys(module_1.default._cache)
|
|
29
|
+
.filter((modulePath) => !loadedModulesBefore.has(modulePath))
|
|
30
|
+
.map((modulePath) => path_1.default.relative(projectRoot, modulePath));
|
|
31
|
+
const ignoredPaths = await loadIgnoredPathsAsync(ignoredFile);
|
|
32
|
+
const filteredLoadedModules = loadedModules.filter((modulePath) => !(0, Path_1.isIgnoredPath)(modulePath, ignoredPaths));
|
|
33
|
+
console.log(JSON.stringify({ config, loadedModules: filteredLoadedModules }));
|
|
34
|
+
}
|
|
35
|
+
// If running from the command line
|
|
36
|
+
if (require.main?.filename === __filename) {
|
|
37
|
+
(async () => {
|
|
38
|
+
const programIndex = process.argv.findIndex((arg) => arg === __filename);
|
|
39
|
+
try {
|
|
40
|
+
await runAsync(process.argv[programIndex], process.argv.slice(programIndex + 1));
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
console.error('Uncaught Error', e);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
})();
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Load the generated ignored paths file from caller and remove the file after loading
|
|
50
|
+
*/
|
|
51
|
+
async function loadIgnoredPathsAsync(ignoredFile) {
|
|
52
|
+
if (!ignoredFile) {
|
|
53
|
+
return Options_1.DEFAULT_IGNORE_PATHS;
|
|
54
|
+
}
|
|
55
|
+
const ignorePaths = [];
|
|
56
|
+
try {
|
|
57
|
+
const fingerprintIgnore = await promises_1.default.readFile(ignoredFile, 'utf8');
|
|
58
|
+
const fingerprintIgnoreLines = fingerprintIgnore.split('\n');
|
|
59
|
+
for (const line of fingerprintIgnoreLines) {
|
|
60
|
+
const trimmedLine = line.trim();
|
|
61
|
+
if (trimmedLine) {
|
|
62
|
+
ignorePaths.push(trimmedLine);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch { }
|
|
67
|
+
try {
|
|
68
|
+
await promises_1.default.rm(ignoredFile);
|
|
69
|
+
}
|
|
70
|
+
catch { }
|
|
71
|
+
return ignorePaths;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get the path to the ExpoConfigLoader file.
|
|
75
|
+
*/
|
|
76
|
+
function getExpoConfigLoaderPath() {
|
|
77
|
+
return path_1.default.join(__dirname, 'ExpoConfigLoader.js');
|
|
78
|
+
}
|
|
79
|
+
exports.getExpoConfigLoaderPath = getExpoConfigLoaderPath;
|
|
80
|
+
//# sourceMappingURL=ExpoConfigLoader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoConfigLoader.js","sourceRoot":"","sources":["../../src/sourcer/ExpoConfigLoader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isIgnoredPath = void 0;
|
|
7
|
+
const minimatch_1 = __importDefault(require("minimatch"));
|
|
8
|
+
/**
|
|
9
|
+
* Indicate the given `filePath` should be excluded by `ignorePaths`
|
|
10
|
+
*/
|
|
11
|
+
function isIgnoredPath(filePath, ignorePaths, minimatchOptions = { dot: true }) {
|
|
12
|
+
const minimatchObjs = ignorePaths.map((ignorePath) => new minimatch_1.default.Minimatch(ignorePath, minimatchOptions));
|
|
13
|
+
let result = false;
|
|
14
|
+
for (const minimatchObj of minimatchObjs) {
|
|
15
|
+
const currMatch = minimatchObj.match(filePath);
|
|
16
|
+
if (minimatchObj.negate && result && !currMatch) {
|
|
17
|
+
// Special handler for negate (!pattern).
|
|
18
|
+
// As long as previous match result is true and not matched from the current negate pattern, we should early return.
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
result ||= currMatch;
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
exports.isIgnoredPath = isIgnoredPath;
|
|
26
|
+
//# sourceMappingURL=Path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Path.js","sourceRoot":"","sources":["../../src/utils/Path.ts"],"names":[],"mappings":";;;;;;AAAA,0DAAkC;AAElC;;GAEG;AACH,SAAgB,aAAa,CAC3B,QAAgB,EAChB,WAAqB,EACrB,mBAAuC,EAAE,GAAG,EAAE,IAAI,EAAE;IAEpD,MAAM,aAAa,GAAG,WAAW,CAAC,GAAG,CACnC,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,mBAAS,CAAC,SAAS,CAAC,UAAU,EAAE,gBAAgB,CAAC,CACtE,CAAC;IAEF,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,KAAK,MAAM,YAAY,IAAI,aAAa,EAAE;QACxC,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,YAAY,CAAC,MAAM,IAAI,MAAM,IAAI,CAAC,SAAS,EAAE;YAC/C,yCAAyC;YACzC,oHAAoH;YACpH,OAAO,KAAK,CAAC;SACd;QACD,MAAM,KAAK,SAAS,CAAC;KACtB;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AApBD,sCAoBC"}
|
|
@@ -6,6 +6,13 @@ import rimraf from 'rimraf';
|
|
|
6
6
|
|
|
7
7
|
import { createProjectHashAsync } from '../../src/Fingerprint';
|
|
8
8
|
|
|
9
|
+
jest.mock('../../src/sourcer/ExpoConfigLoader', () => ({
|
|
10
|
+
// Mock the getExpoConfigLoaderPath to use the built version rather than the typescript version from src
|
|
11
|
+
getExpoConfigLoaderPath: jest.fn(() =>
|
|
12
|
+
path.resolve(__dirname, '..', '..', 'build', 'sourcer', 'ExpoConfigLoader.js')
|
|
13
|
+
),
|
|
14
|
+
}));
|
|
15
|
+
|
|
9
16
|
describe('bare project test', () => {
|
|
10
17
|
jest.setTimeout(600000);
|
|
11
18
|
const tmpDir = os.tmpdir();
|
|
@@ -11,6 +11,13 @@ import {
|
|
|
11
11
|
import { normalizeOptionsAsync } from '../../src/Options';
|
|
12
12
|
import { getHashSourcesAsync } from '../../src/sourcer/Sourcer';
|
|
13
13
|
|
|
14
|
+
jest.mock('../../src/sourcer/ExpoConfigLoader', () => ({
|
|
15
|
+
// Mock the getExpoConfigLoaderPath to use the built version rather than the typescript version from src
|
|
16
|
+
getExpoConfigLoaderPath: jest.fn(() =>
|
|
17
|
+
path.resolve(__dirname, '..', '..', 'build', 'sourcer', 'ExpoConfigLoader.js')
|
|
18
|
+
),
|
|
19
|
+
}));
|
|
20
|
+
|
|
14
21
|
describe('managed project test', () => {
|
|
15
22
|
jest.setTimeout(600000);
|
|
16
23
|
const tmpDir = require('temp-dir');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expo/fingerprint",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "A library to generate a fingerprint from a React Native project",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"clean": "rimraf build ./tsconfig.tsbuildinfo",
|
|
12
12
|
"lint": "eslint .",
|
|
13
13
|
"test": "jest",
|
|
14
|
-
"test:e2e": "jest --config e2e/jest.config.js"
|
|
14
|
+
"test:e2e": "yarn prepare && jest --config e2e/jest.config.js"
|
|
15
15
|
},
|
|
16
16
|
"bin": {
|
|
17
17
|
"fingerprint": "bin/cli.js"
|
|
@@ -48,5 +48,5 @@
|
|
|
48
48
|
"glob": "^7.1.7",
|
|
49
49
|
"temp-dir": "^2.0.0"
|
|
50
50
|
},
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "ee7897097f5f946ad7fcb94447eed789b984dd02"
|
|
52
52
|
}
|
package/src/Options.ts
CHANGED
|
@@ -18,6 +18,31 @@ export const DEFAULT_IGNORE_PATHS = [
|
|
|
18
18
|
'app.config.js',
|
|
19
19
|
'app.config.json',
|
|
20
20
|
'app.json',
|
|
21
|
+
|
|
22
|
+
// Ignore default javascript files when calling `getConfig()`
|
|
23
|
+
'**/node_modules/@babel/**/*',
|
|
24
|
+
'**/node_modules/@expo/**/*',
|
|
25
|
+
'**/node_modules/@jridgewell/**/*',
|
|
26
|
+
'**/node_modules/expo/config.js',
|
|
27
|
+
'**/node_modules/expo/config-plugins.js',
|
|
28
|
+
`**/node_modules/{${[
|
|
29
|
+
'debug',
|
|
30
|
+
'escape-string-regexp',
|
|
31
|
+
'getenv',
|
|
32
|
+
'graceful-fs',
|
|
33
|
+
'has-flag',
|
|
34
|
+
'imurmurhash',
|
|
35
|
+
'js-tokens',
|
|
36
|
+
'json5',
|
|
37
|
+
'lines-and-columns',
|
|
38
|
+
'require-from-string',
|
|
39
|
+
'resolve-from',
|
|
40
|
+
'signal-exit',
|
|
41
|
+
'sucrase',
|
|
42
|
+
'supports-color',
|
|
43
|
+
'ts-interface-checker',
|
|
44
|
+
'write-file-atomic',
|
|
45
|
+
].join(',')}}/**/*`,
|
|
21
46
|
];
|
|
22
47
|
|
|
23
48
|
export async function normalizeOptionsAsync(
|
package/src/hash/Hash.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'crypto';
|
|
2
2
|
import { createReadStream } from 'fs';
|
|
3
3
|
import fs from 'fs/promises';
|
|
4
|
-
import minimatch from 'minimatch';
|
|
5
4
|
import pLimit from 'p-limit';
|
|
6
5
|
import path from 'path';
|
|
7
6
|
|
|
@@ -13,6 +12,7 @@ import type {
|
|
|
13
12
|
HashSourceContents,
|
|
14
13
|
NormalizedOptions,
|
|
15
14
|
} from '../Fingerprint.types';
|
|
15
|
+
import { isIgnoredPath } from '../utils/Path';
|
|
16
16
|
import { profile } from '../utils/Profile';
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -131,31 +131,6 @@ export async function createFileHashResultsAsync(
|
|
|
131
131
|
});
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
/**
|
|
135
|
-
* Indicate the given `filePath` should be excluded by `ignorePaths`
|
|
136
|
-
*/
|
|
137
|
-
export function isIgnoredPath(
|
|
138
|
-
filePath: string,
|
|
139
|
-
ignorePaths: string[],
|
|
140
|
-
minimatchOptions: minimatch.IOptions = { dot: true }
|
|
141
|
-
): boolean {
|
|
142
|
-
const minimatchObjs = ignorePaths.map(
|
|
143
|
-
(ignorePath) => new minimatch.Minimatch(ignorePath, minimatchOptions)
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
let result = false;
|
|
147
|
-
for (const minimatchObj of minimatchObjs) {
|
|
148
|
-
const currMatch = minimatchObj.match(filePath);
|
|
149
|
-
if (minimatchObj.negate && result && !currMatch) {
|
|
150
|
-
// Special handler for negate (!pattern).
|
|
151
|
-
// As long as previous match result is true and not matched from the current negate pattern, we should early return.
|
|
152
|
-
return false;
|
|
153
|
-
}
|
|
154
|
-
result ||= currMatch;
|
|
155
|
-
}
|
|
156
|
-
return result;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
134
|
/**
|
|
160
135
|
* Create `HashResult` for a dir.
|
|
161
136
|
* If the dir is excluded, returns null rather than a HashResult
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
createFingerprintFromSourcesAsync,
|
|
13
13
|
createFingerprintSourceAsync,
|
|
14
14
|
createSourceId,
|
|
15
|
-
isIgnoredPath,
|
|
16
15
|
} from '../Hash';
|
|
17
16
|
|
|
18
17
|
jest.mock('fs');
|
|
@@ -237,38 +236,3 @@ describe(createSourceId, () => {
|
|
|
237
236
|
expect(createSourceId(source)).toBe('foo');
|
|
238
237
|
});
|
|
239
238
|
});
|
|
240
|
-
|
|
241
|
-
describe(isIgnoredPath, () => {
|
|
242
|
-
it('should support file pattern', () => {
|
|
243
|
-
expect(isIgnoredPath('app.json', ['app.json'])).toBe(true);
|
|
244
|
-
expect(isIgnoredPath('app.ts', ['*.{js,ts}'])).toBe(true);
|
|
245
|
-
expect(isIgnoredPath('/dir/app.json', ['/dir/*.json'])).toBe(true);
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
it('should support directory pattern', () => {
|
|
249
|
-
expect(isIgnoredPath('/app/ios/Podfile', ['**/ios/**/*'])).toBe(true);
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
it('case sensitive by design', () => {
|
|
253
|
-
expect(isIgnoredPath('app.json', ['APP.JSON'])).toBe(false);
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
it('should include dot files from wildcard pattern', () => {
|
|
257
|
-
expect(isIgnoredPath('.bashrc', ['*'])).toBe(true);
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
it('no `matchBase` and `partial` by design', () => {
|
|
261
|
-
expect(isIgnoredPath('/dir/app.json', ['app.json'])).toBe(false);
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
it('match a file inside a dir should use a globstar', () => {
|
|
265
|
-
expect(isIgnoredPath('/dir/app.ts', ['*'])).toBe(false);
|
|
266
|
-
expect(isIgnoredPath('/dir/app.ts', ['**/*'])).toBe(true);
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
it('should use `!` to override default ignorePaths', () => {
|
|
270
|
-
const ignorePaths = ['**/ios/**/*', '!**/ios/Podfile', '**/android/**/*'];
|
|
271
|
-
expect(isIgnoredPath('/app/ios/Podfile', ignorePaths)).toBe(false);
|
|
272
|
-
expect(isIgnoredPath('/app/ios/Podfile.lock', ignorePaths)).toBe(true);
|
|
273
|
-
});
|
|
274
|
-
});
|
package/src/sourcer/Expo.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import spawnAsync from '@expo/spawn-async';
|
|
2
|
-
import assert from 'assert';
|
|
3
2
|
import chalk from 'chalk';
|
|
4
3
|
import type { ExpoConfig, ProjectConfig } from 'expo/config';
|
|
5
|
-
import
|
|
4
|
+
import fs from 'fs/promises';
|
|
5
|
+
import os from 'os';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import resolveFrom from 'resolve-from';
|
|
8
8
|
|
|
9
|
+
import { getExpoConfigLoaderPath } from './ExpoConfigLoader';
|
|
9
10
|
import { getFileBasedHashSourceAsync, stringifyJsonSorted } from './Utils';
|
|
10
11
|
import type { HashSource, NormalizedOptions } from '../Fingerprint.types';
|
|
11
12
|
|
|
@@ -15,12 +16,23 @@ export async function getExpoConfigSourcesAsync(
|
|
|
15
16
|
projectRoot: string,
|
|
16
17
|
options: NormalizedOptions
|
|
17
18
|
): Promise<HashSource[]> {
|
|
18
|
-
|
|
19
|
+
if (!resolveFrom.silent(path.resolve(projectRoot), 'expo/config')) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
19
22
|
|
|
23
|
+
const results: HashSource[] = [];
|
|
20
24
|
let config: ProjectConfig;
|
|
25
|
+
let loadedModules: string[] = [];
|
|
26
|
+
const ignoredFile = await createTempIgnoredFileAsync(options);
|
|
21
27
|
try {
|
|
22
|
-
const {
|
|
23
|
-
|
|
28
|
+
const { stdout } = await spawnAsync(
|
|
29
|
+
'node',
|
|
30
|
+
[getExpoConfigLoaderPath(), path.resolve(projectRoot), ignoredFile],
|
|
31
|
+
{ cwd: __dirname }
|
|
32
|
+
);
|
|
33
|
+
const stdoutJson = JSON.parse(stdout);
|
|
34
|
+
config = stdoutJson.config;
|
|
35
|
+
loadedModules = stdoutJson.loadedModules;
|
|
24
36
|
results.push({
|
|
25
37
|
type: 'contents',
|
|
26
38
|
id: 'expoConfig',
|
|
@@ -28,7 +40,9 @@ export async function getExpoConfigSourcesAsync(
|
|
|
28
40
|
reasons: ['expoConfig'],
|
|
29
41
|
});
|
|
30
42
|
} catch (e: unknown) {
|
|
31
|
-
|
|
43
|
+
if (e instanceof Error) {
|
|
44
|
+
console.warn(`Cannot get Expo config from an Expo project - ${e.message}: `, e.stack);
|
|
45
|
+
}
|
|
32
46
|
return [];
|
|
33
47
|
}
|
|
34
48
|
|
|
@@ -77,19 +91,16 @@ export async function getExpoConfigSourcesAsync(
|
|
|
77
91
|
results.push(...externalFileSources);
|
|
78
92
|
|
|
79
93
|
// config plugins
|
|
80
|
-
const
|
|
81
|
-
|
|
94
|
+
const configPluginModules: HashSource[] = loadedModules.map((modulePath) => ({
|
|
95
|
+
type: 'file',
|
|
96
|
+
filePath: modulePath,
|
|
97
|
+
reasons: ['expoConfigPlugins'],
|
|
98
|
+
}));
|
|
99
|
+
results.push(...configPluginModules);
|
|
82
100
|
|
|
83
101
|
return results;
|
|
84
102
|
}
|
|
85
103
|
|
|
86
|
-
function findUpPluginRoot(entryFile: string): string {
|
|
87
|
-
const entryRoot = path.dirname(entryFile);
|
|
88
|
-
const packageJson = findUp.sync('package.json', { cwd: path.dirname(entryFile) });
|
|
89
|
-
assert(packageJson, `No package.json found for module "${entryRoot}"`);
|
|
90
|
-
return path.dirname(packageJson);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
104
|
function normalizeExpoConfig(config: ExpoConfig): string {
|
|
94
105
|
// Deep clone by JSON.parse/stringify that assumes the config is serializable.
|
|
95
106
|
const normalizedConfig: ExpoConfig = JSON.parse(JSON.stringify(config));
|
|
@@ -98,31 +109,14 @@ function normalizeExpoConfig(config: ExpoConfig): string {
|
|
|
98
109
|
return stringifyJsonSorted(normalizedConfig);
|
|
99
110
|
}
|
|
100
111
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
):
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const reasons = ['expoConfigPlugins'];
|
|
110
|
-
const nullableResults: (HashSource | null)[] = plugins.map((plugin) => {
|
|
111
|
-
const pluginPackageName = Array.isArray(plugin) ? plugin[0] : plugin;
|
|
112
|
-
if (typeof pluginPackageName === 'string') {
|
|
113
|
-
const pluginPackageEntryFile = resolveFrom.silent(projectRoot, pluginPackageName);
|
|
114
|
-
const pluginPackageRoot = pluginPackageEntryFile
|
|
115
|
-
? findUpPluginRoot(pluginPackageEntryFile)
|
|
116
|
-
: null;
|
|
117
|
-
if (pluginPackageRoot) {
|
|
118
|
-
debug(`Adding config-plugin root - ${chalk.dim(pluginPackageRoot)}`);
|
|
119
|
-
return { type: 'dir', filePath: path.relative(projectRoot, pluginPackageRoot), reasons };
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
return null;
|
|
123
|
-
});
|
|
124
|
-
const results = nullableResults.filter(Boolean) as HashSource[];
|
|
125
|
-
return results;
|
|
112
|
+
/**
|
|
113
|
+
* Create a temporary file with ignored paths from options that will be read by the ExpoConfigLoader.
|
|
114
|
+
*/
|
|
115
|
+
async function createTempIgnoredFileAsync(options: NormalizedOptions): Promise<string> {
|
|
116
|
+
await fs.mkdtemp(path.join(os.tmpdir(), 'expo-fingerprint-'));
|
|
117
|
+
const ignoredFile = path.join(os.tmpdir(), '.fingerprintignore');
|
|
118
|
+
await fs.writeFile(ignoredFile, options.ignorePaths.join('\n'));
|
|
119
|
+
return ignoredFile;
|
|
126
120
|
}
|
|
127
121
|
|
|
128
122
|
export async function getEasBuildSourcesAsync(projectRoot: string, options: NormalizedOptions) {
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A helper script to load the Expo config and loaded plugins from a project
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from 'fs/promises';
|
|
6
|
+
import module from 'module';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import resolveFrom from 'resolve-from';
|
|
9
|
+
|
|
10
|
+
import { DEFAULT_IGNORE_PATHS } from '../Options';
|
|
11
|
+
import { isIgnoredPath } from '../utils/Path';
|
|
12
|
+
|
|
13
|
+
async function runAsync(programName: string, args: string[] = []) {
|
|
14
|
+
if (args.length < 1) {
|
|
15
|
+
console.log(`Usage: ${programName} <projectRoot> [ignoredFile]`);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const projectRoot = path.resolve(args[0]);
|
|
20
|
+
const ignoredFile = args[1] ? path.resolve(args[1]) : null;
|
|
21
|
+
|
|
22
|
+
// @ts-expect-error: module internal _cache
|
|
23
|
+
const loadedModulesBefore = new Set(Object.keys(module._cache));
|
|
24
|
+
|
|
25
|
+
const { getConfig } = require(resolveFrom(path.resolve(projectRoot), 'expo/config'));
|
|
26
|
+
const config = await getConfig(projectRoot, { skipSDKVersionRequirement: true });
|
|
27
|
+
// @ts-expect-error: module internal _cache
|
|
28
|
+
const loadedModules = Object.keys(module._cache)
|
|
29
|
+
.filter((modulePath) => !loadedModulesBefore.has(modulePath))
|
|
30
|
+
.map((modulePath) => path.relative(projectRoot, modulePath));
|
|
31
|
+
|
|
32
|
+
const ignoredPaths = await loadIgnoredPathsAsync(ignoredFile);
|
|
33
|
+
const filteredLoadedModules = loadedModules.filter(
|
|
34
|
+
(modulePath) => !isIgnoredPath(modulePath, ignoredPaths)
|
|
35
|
+
);
|
|
36
|
+
console.log(JSON.stringify({ config, loadedModules: filteredLoadedModules }));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// If running from the command line
|
|
40
|
+
if (require.main?.filename === __filename) {
|
|
41
|
+
(async () => {
|
|
42
|
+
const programIndex = process.argv.findIndex((arg) => arg === __filename);
|
|
43
|
+
try {
|
|
44
|
+
await runAsync(process.argv[programIndex], process.argv.slice(programIndex + 1));
|
|
45
|
+
} catch (e) {
|
|
46
|
+
console.error('Uncaught Error', e);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
})();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Load the generated ignored paths file from caller and remove the file after loading
|
|
54
|
+
*/
|
|
55
|
+
async function loadIgnoredPathsAsync(ignoredFile: string | null) {
|
|
56
|
+
if (!ignoredFile) {
|
|
57
|
+
return DEFAULT_IGNORE_PATHS;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const ignorePaths = [];
|
|
61
|
+
try {
|
|
62
|
+
const fingerprintIgnore = await fs.readFile(ignoredFile, 'utf8');
|
|
63
|
+
const fingerprintIgnoreLines = fingerprintIgnore.split('\n');
|
|
64
|
+
for (const line of fingerprintIgnoreLines) {
|
|
65
|
+
const trimmedLine = line.trim();
|
|
66
|
+
if (trimmedLine) {
|
|
67
|
+
ignorePaths.push(trimmedLine);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} catch {}
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
await fs.rm(ignoredFile);
|
|
74
|
+
} catch {}
|
|
75
|
+
|
|
76
|
+
return ignorePaths;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get the path to the ExpoConfigLoader file.
|
|
81
|
+
*/
|
|
82
|
+
export function getExpoConfigLoaderPath() {
|
|
83
|
+
return path.join(__dirname, 'ExpoConfigLoader.js');
|
|
84
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import spawnAsync from '@expo/spawn-async';
|
|
2
|
-
import
|
|
2
|
+
import { getConfig } from 'expo/config';
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import { vol, fs as volFS } from 'memfs';
|
|
5
5
|
import path from 'path';
|
|
@@ -148,9 +148,9 @@ describe(getExpoConfigSourcesAsync, () => {
|
|
|
148
148
|
|
|
149
149
|
it('should return empty array when expo package is not installed', async () => {
|
|
150
150
|
vol.fromJSON(require('./fixtures/BareReactNative70Project.json'));
|
|
151
|
-
const mockedResolveFrom = resolveFrom as jest.MockedFunction<typeof resolveFrom>;
|
|
151
|
+
const mockedResolveFrom = resolveFrom.silent as jest.MockedFunction<typeof resolveFrom.silent>;
|
|
152
152
|
mockedResolveFrom.mockImplementationOnce((fromDirectory: string, moduleId: string) => {
|
|
153
|
-
const actualResolver = jest.requireActual('resolve-from');
|
|
153
|
+
const actualResolver = jest.requireActual('resolve-from').silent;
|
|
154
154
|
// To fake the case as no expo installed, trying to resolve as **nonexist/expo/config** module
|
|
155
155
|
return actualResolver(fromDirectory, 'nonexist/expo/config');
|
|
156
156
|
});
|
|
@@ -222,115 +222,39 @@ export default ({ config }) => {
|
|
|
222
222
|
})
|
|
223
223
|
);
|
|
224
224
|
});
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
describe(`getExpoConfigSourcesAsync - config-plugins`, () => {
|
|
228
|
-
let baseAppJson: { expo: any };
|
|
229
|
-
|
|
230
|
-
function setupThirdPartyPlugin() {
|
|
231
|
-
vol.mkdirSync('/app/node_modules/third-party', { recursive: true });
|
|
232
|
-
|
|
233
|
-
// package.json
|
|
234
|
-
vol.writeFileSync('/app/node_modules/third-party/package.json', '{}');
|
|
235
|
-
const mockFindUpSync = findUp.sync as jest.MockedFunction<typeof findUp.sync>;
|
|
236
|
-
mockFindUpSync.mockReturnValue('/app/node_modules/third-party/package.json');
|
|
237
225
|
|
|
238
|
-
|
|
239
|
-
const withNoopPlugin = (config: any) => config;
|
|
240
|
-
jest.mock('/app/node_modules/third-party/index.js', () => ({ default: withNoopPlugin }), {
|
|
241
|
-
virtual: true,
|
|
242
|
-
});
|
|
243
|
-
const mockResolveFrom = resolveFrom.silent as jest.MockedFunction<typeof resolveFrom.silent>;
|
|
244
|
-
mockResolveFrom.mockReturnValue('/app/node_modules/third-party/index.js');
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
beforeEach(() => {
|
|
248
|
-
jest.doMock('fs', () => volFS);
|
|
226
|
+
it('should contain extra files from config plugins', async () => {
|
|
249
227
|
vol.fromJSON(require('./fixtures/ExpoManaged47Project.json'));
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
expo: {
|
|
267
|
-
...baseAppJson.expo,
|
|
268
|
-
plugins: ['third-party'],
|
|
269
|
-
},
|
|
270
|
-
})
|
|
271
|
-
);
|
|
228
|
+
const config = await getConfig('/app', { skipSDKVersionRequirement: true });
|
|
229
|
+
const mockSpawnAsync = spawnAsync as jest.MockedFunction<typeof spawnAsync>;
|
|
230
|
+
const stdout = JSON.stringify({
|
|
231
|
+
config,
|
|
232
|
+
loadedModules: [
|
|
233
|
+
'node_modules/third-party/index.js',
|
|
234
|
+
'node_modules/third-party/node_modules/transitive-third-party/index.js',
|
|
235
|
+
],
|
|
236
|
+
});
|
|
237
|
+
mockSpawnAsync.mockResolvedValueOnce({
|
|
238
|
+
output: [],
|
|
239
|
+
stdout,
|
|
240
|
+
stderr: '',
|
|
241
|
+
signal: null,
|
|
242
|
+
status: 0,
|
|
243
|
+
});
|
|
272
244
|
const sources = await getExpoConfigSourcesAsync('/app', await normalizeOptionsAsync('/app'));
|
|
273
245
|
expect(sources).toContainEqual(
|
|
274
246
|
expect.objectContaining({
|
|
275
|
-
type: '
|
|
276
|
-
filePath: 'node_modules/third-party',
|
|
277
|
-
})
|
|
278
|
-
);
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
it('should contain external config-plugin dir from plugin with parameters', async () => {
|
|
282
|
-
setupThirdPartyPlugin();
|
|
283
|
-
|
|
284
|
-
vol.writeFileSync(
|
|
285
|
-
'/app/app.json',
|
|
286
|
-
JSON.stringify({
|
|
287
|
-
...baseAppJson,
|
|
288
|
-
expo: {
|
|
289
|
-
...baseAppJson.expo,
|
|
290
|
-
plugins: [['third-party', { parameter: 'foo' }]],
|
|
291
|
-
},
|
|
247
|
+
type: 'file',
|
|
248
|
+
filePath: 'node_modules/third-party/index.js',
|
|
292
249
|
})
|
|
293
250
|
);
|
|
294
|
-
const sources = await getExpoConfigSourcesAsync('/app', await normalizeOptionsAsync('/app'));
|
|
295
251
|
expect(sources).toContainEqual(
|
|
296
252
|
expect.objectContaining({
|
|
297
|
-
type: '
|
|
298
|
-
filePath: 'node_modules/third-party',
|
|
253
|
+
type: 'file',
|
|
254
|
+
filePath: 'node_modules/third-party/node_modules/transitive-third-party/index.js',
|
|
299
255
|
})
|
|
300
256
|
);
|
|
301
257
|
});
|
|
302
|
-
|
|
303
|
-
it('should not contain external config-plugin dir from raw function plugins', async () => {
|
|
304
|
-
vol.writeFileSync(
|
|
305
|
-
'/app/app.config.js',
|
|
306
|
-
`\
|
|
307
|
-
export default ({ config }) => {
|
|
308
|
-
return config;
|
|
309
|
-
};`
|
|
310
|
-
);
|
|
311
|
-
const sources = await getExpoConfigSourcesAsync('/app', await normalizeOptionsAsync('/app'));
|
|
312
|
-
|
|
313
|
-
vol.writeFileSync(
|
|
314
|
-
'/app/app.config.js',
|
|
315
|
-
`\
|
|
316
|
-
export default ({ config }) => {
|
|
317
|
-
config.plugins ||= [];
|
|
318
|
-
const withNoopPlugin = (config: any) => config;
|
|
319
|
-
config.plugins.push(withNoopPlugin);
|
|
320
|
-
return config;
|
|
321
|
-
};`
|
|
322
|
-
);
|
|
323
|
-
const sources2 = await getExpoConfigSourcesAsync('/app', await normalizeOptionsAsync('/app'));
|
|
324
|
-
|
|
325
|
-
// sources2 will contain the plugins from expo config, but sources will not.
|
|
326
|
-
const sourcesWithoutExpoConfig = sources.filter(
|
|
327
|
-
(item) => item.type !== 'contents' || item.id !== 'expoConfig'
|
|
328
|
-
);
|
|
329
|
-
const sources2WithoutExpoConfig = sources2.filter(
|
|
330
|
-
(item) => item.type !== 'contents' || item.id !== 'expoConfig'
|
|
331
|
-
);
|
|
332
|
-
expect(sourcesWithoutExpoConfig).toEqual(sources2WithoutExpoConfig);
|
|
333
|
-
});
|
|
334
258
|
});
|
|
335
259
|
|
|
336
260
|
describe('sortExpoAutolinkingConfig', () => {
|
|
@@ -4,6 +4,8 @@ import { normalizeOptionsAsync } from '../../Options';
|
|
|
4
4
|
import { getPatchPackageSourcesAsync } from '../PatchPackage';
|
|
5
5
|
import { getHashSourcesAsync } from '../Sourcer';
|
|
6
6
|
|
|
7
|
+
jest.mock('@expo/spawn-async');
|
|
8
|
+
jest.mock('fs');
|
|
7
9
|
jest.mock('fs/promises');
|
|
8
10
|
jest.mock('/app/package.json', () => ({}), { virtual: true });
|
|
9
11
|
|
|
@@ -28,6 +30,7 @@ describe(getPatchPackageSourcesAsync, () => {
|
|
|
28
30
|
|
|
29
31
|
describe('patch-package postinstall', () => {
|
|
30
32
|
it('should contain `package.json` scripts block for lifecycle patches', async () => {
|
|
33
|
+
vol.fromJSON(require('./fixtures/ExpoManaged47Project.json'));
|
|
31
34
|
const scriptsBlock = {
|
|
32
35
|
postinstall: 'npx patch-package',
|
|
33
36
|
};
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
import { vol } from 'memfs';
|
|
2
|
+
|
|
1
3
|
import { normalizeOptionsAsync } from '../../Options';
|
|
2
4
|
import { getHashSourcesAsync } from '../Sourcer';
|
|
3
5
|
|
|
6
|
+
jest.mock('@expo/spawn-async');
|
|
7
|
+
jest.mock('fs');
|
|
8
|
+
jest.mock('fs/promises');
|
|
9
|
+
|
|
4
10
|
describe(getHashSourcesAsync, () => {
|
|
5
11
|
it('should include `extraSources` from input parameter', async () => {
|
|
12
|
+
vol.fromJSON(require('./fixtures/ExpoManaged47Project.json'));
|
|
6
13
|
const sources = await getHashSourcesAsync(
|
|
7
14
|
'/app',
|
|
8
15
|
await normalizeOptionsAsync('/app', {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import minimatch from 'minimatch';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Indicate the given `filePath` should be excluded by `ignorePaths`
|
|
5
|
+
*/
|
|
6
|
+
export function isIgnoredPath(
|
|
7
|
+
filePath: string,
|
|
8
|
+
ignorePaths: string[],
|
|
9
|
+
minimatchOptions: minimatch.IOptions = { dot: true }
|
|
10
|
+
): boolean {
|
|
11
|
+
const minimatchObjs = ignorePaths.map(
|
|
12
|
+
(ignorePath) => new minimatch.Minimatch(ignorePath, minimatchOptions)
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
let result = false;
|
|
16
|
+
for (const minimatchObj of minimatchObjs) {
|
|
17
|
+
const currMatch = minimatchObj.match(filePath);
|
|
18
|
+
if (minimatchObj.negate && result && !currMatch) {
|
|
19
|
+
// Special handler for negate (!pattern).
|
|
20
|
+
// As long as previous match result is true and not matched from the current negate pattern, we should early return.
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
result ||= currMatch;
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { isIgnoredPath } from '../Path';
|
|
2
|
+
|
|
3
|
+
describe(isIgnoredPath, () => {
|
|
4
|
+
it('should support file pattern', () => {
|
|
5
|
+
expect(isIgnoredPath('app.json', ['app.json'])).toBe(true);
|
|
6
|
+
expect(isIgnoredPath('app.ts', ['*.{js,ts}'])).toBe(true);
|
|
7
|
+
expect(isIgnoredPath('/dir/app.json', ['/dir/*.json'])).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should support directory pattern', () => {
|
|
11
|
+
expect(isIgnoredPath('/app/ios/Podfile', ['**/ios/**/*'])).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('case sensitive by design', () => {
|
|
15
|
+
expect(isIgnoredPath('app.json', ['APP.JSON'])).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should include dot files from wildcard pattern', () => {
|
|
19
|
+
expect(isIgnoredPath('.bashrc', ['*'])).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('no `matchBase` and `partial` by design', () => {
|
|
23
|
+
expect(isIgnoredPath('/dir/app.json', ['app.json'])).toBe(false);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('match a file inside a dir should use a globstar', () => {
|
|
27
|
+
expect(isIgnoredPath('/dir/app.ts', ['*'])).toBe(false);
|
|
28
|
+
expect(isIgnoredPath('/dir/app.ts', ['**/*'])).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should use `!` to override default ignorePaths', () => {
|
|
32
|
+
const ignorePaths = ['**/ios/**/*', '!**/ios/Podfile', '**/android/**/*'];
|
|
33
|
+
expect(isIgnoredPath('/app/ios/Podfile', ignorePaths)).toBe(false);
|
|
34
|
+
expect(isIgnoredPath('/app/ios/Podfile.lock', ignorePaths)).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
});
|