chore: add mobile app

This commit is contained in:
2026-02-07 22:33:02 +01:00
parent db34640acc
commit e89384ad4e
29327 changed files with 3886944 additions and 12 deletions
+22
View File
@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015-present 650 Industries, Inc. (aka Expo)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+19
View File
@@ -0,0 +1,19 @@
<p>
<a href="https://docs.expo.dev/versions/latest/sdk/fingerprint/">
<img
src="../../../.github/resources/expo-fingerprint.svg"
alt="@expo/fingerprint"
height="64" />
</a>
</p>
A library to generate a fingerprint from a React Native project
# API documentation
- [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/)
# Contributing
Contributions are very welcome! Please refer to guidelines described in the [contributing guide](https://github.com/expo/expo#contributing).
+3
View File
@@ -0,0 +1,3 @@
#!/usr/bin/env node
require('../cli/build/cli.js');
+17
View File
@@ -0,0 +1,17 @@
import type { Config } from './Fingerprint.types';
import { SourceSkips } from './sourcer/SourceSkips';
type NormalizedConfig = Config & {
sourceSkips?: SourceSkips;
};
/**
* Load the fingerprint.config.js from project root.
* @param projectRoot The project root directory.
* @param silent Whether to mute console logs when loading the config. This is useful for expo-updates integration and makes sure the JSON output is valid.
* @returns The loaded config or null if no config file was found.
*/
export declare function loadConfigAsync(projectRoot: string, silent?: boolean): Promise<NormalizedConfig | null>;
/**
* Normalize the sourceSkips from enum number or string array to a valid enum number.
*/
export declare function normalizeSourceSkips(sourceSkips: Config['sourceSkips']): SourceSkips;
export {};
+120
View File
@@ -0,0 +1,120 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadConfigAsync = loadConfigAsync;
exports.normalizeSourceSkips = normalizeSourceSkips;
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
const SourceSkips_1 = require("./sourcer/SourceSkips");
const CONFIG_FILES = ['fingerprint.config.js', 'fingerprint.config.cjs'];
const debug = require('debug')('expo:fingerprint:Config');
/**
* Load the fingerprint.config.js from project root.
* @param projectRoot The project root directory.
* @param silent Whether to mute console logs when loading the config. This is useful for expo-updates integration and makes sure the JSON output is valid.
* @returns The loaded config or null if no config file was found.
*/
async function loadConfigAsync(projectRoot, silent = false) {
let configFile;
try {
configFile = await resolveConfigFileAsync(projectRoot);
}
catch {
return null;
}
debug('Resolved config file:', configFile);
const unregisterMuteLogs = silent ? muteLogs() : null;
let rawConfig;
try {
rawConfig = require(configFile);
}
catch (e) {
debug('Error loading config file:', e);
rawConfig = {};
}
unregisterMuteLogs?.();
const supportedConfigKeys = [
'concurrentIoLimit',
'hashAlgorithm',
'ignorePaths',
'extraSources',
'sourceSkips',
'enableReactImportsPatcher',
'useRNCoreAutolinkingFromExpo',
'debug',
'fileHookTransform',
];
const config = {};
for (const key of supportedConfigKeys) {
if (key in rawConfig) {
if (key === 'sourceSkips') {
config[key] = normalizeSourceSkips(rawConfig[key]);
}
else {
config[key] = rawConfig[key];
}
}
}
return config;
}
/**
* Normalize the sourceSkips from enum number or string array to a valid enum number.
*/
function normalizeSourceSkips(sourceSkips) {
if (sourceSkips == null) {
return SourceSkips_1.SourceSkips.None;
}
if (typeof sourceSkips === 'number') {
return sourceSkips;
}
if (Array.isArray(sourceSkips)) {
let result = SourceSkips_1.SourceSkips.None;
for (const value of sourceSkips) {
if (typeof value !== 'string') {
continue;
}
const skipValue = SourceSkips_1.SourceSkips[value];
if (skipValue != null) {
result |= skipValue;
}
}
return result;
}
throw new Error(`Invalid sourceSkips type: ${sourceSkips}`);
}
/**
* Resolve the config file path from the project root.
*/
async function resolveConfigFileAsync(projectRoot) {
return await Promise.any(CONFIG_FILES.map(async (file) => {
const configPath = path_1.default.resolve(projectRoot, file);
const stat = await promises_1.default.stat(configPath);
if (!stat.isFile()) {
throw new Error(`Config file is not a file: ${configPath}`);
}
return configPath;
}));
}
/**
* Monkey-patch the console to mute logs.
* @returns A function to unregister the monkey-patch.
*/
function muteLogs() {
const originalConsole = {
log: console.log,
warn: console.warn,
error: console.error,
};
const unregister = () => {
console.log = originalConsole.log;
console.warn = originalConsole.warn;
console.error = originalConsole.error;
};
console.log = () => { };
console.warn = () => { };
console.error = () => { };
return unregister;
}
//# sourceMappingURL=Config.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Config.js","sourceRoot":"","sources":["../src/Config.ts"],"names":[],"mappings":"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"}
+9
View File
@@ -0,0 +1,9 @@
import type { HashSource } from './Fingerprint.types';
/**
* Strip duplicated sources, mainly for duplicated file or dir
*/
export declare function dedupSources(sources: HashSource[], projectRoot: string): HashSource[];
/**
* When two sources are duplicated, merge `src`'s reasons into `dst`
*/
export declare function mergeSourceWithReasons(dst: HashSource, src: HashSource): HashSource;
+82
View File
@@ -0,0 +1,82 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.dedupSources = dedupSources;
exports.mergeSourceWithReasons = mergeSourceWithReasons;
const assert_1 = __importDefault(require("assert"));
const path_1 = __importDefault(require("path"));
const debug = require('debug')('expo:fingerprint:Dedup');
/**
* Strip duplicated sources, mainly for duplicated file or dir
*/
function dedupSources(sources, projectRoot) {
const newSources = [];
for (const source of sources) {
const [duplicatedItemIndex, shouldSwapSource] = findDuplicatedSourceIndex(newSources, source, projectRoot);
if (duplicatedItemIndex >= 0) {
const duplicatedItem = newSources[duplicatedItemIndex];
debug(`Skipping duplicated source: ${JSON.stringify(source)}`);
if (shouldSwapSource) {
newSources[duplicatedItemIndex] = {
...source,
reasons: [...source.reasons, ...duplicatedItem.reasons],
};
}
else {
duplicatedItem.reasons = [...duplicatedItem.reasons, ...source.reasons];
}
}
else {
newSources.push(source);
}
}
return newSources;
}
/**
* When two sources are duplicated, merge `src`'s reasons into `dst`
*/
function mergeSourceWithReasons(dst, src) {
return dst;
}
/**
* Find the duplicated `source` in `newSources`
* @return tuple of [duplicatedItemIndexInNewSources, shouldSwapSource]
*/
function findDuplicatedSourceIndex(newSources, source, projectRoot) {
let shouldSwapSource = false;
if (source.type === 'contents') {
return [
newSources.findIndex((item) => item.type === source.type && item.id === source.id) ?? null,
shouldSwapSource,
];
}
for (const [index, existingSource] of newSources.entries()) {
if (existingSource.type === 'contents') {
continue;
}
if (isDescendant(source, existingSource, projectRoot)) {
return [index, shouldSwapSource];
}
// If the new source is ancestor of existing source, replace swap the existing source with the new source
if (isDescendant(existingSource, source, projectRoot)) {
shouldSwapSource = true;
return [index, shouldSwapSource];
}
}
return [-1, shouldSwapSource];
}
function isDescendant(from, to, projectRoot) {
if (from === to) {
return true;
}
const fromPath = path_1.default.join(projectRoot, from.filePath);
const toPath = path_1.default.join(projectRoot, to.filePath);
const result = path_1.default.relative(fromPath, toPath).match(/^[./\\/]*$/) != null;
if (result) {
(0, assert_1.default)(!(to.type === 'file' && from.type === 'dir'), `Unexpected case which a dir is a descendant of a file - from[${fromPath}] to[${toPath}]`);
}
return result;
}
//# sourceMappingURL=Dedup.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Dedup.js","sourceRoot":"","sources":["../src/Dedup.ts"],"names":[],"mappings":"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"}
+9
View File
@@ -0,0 +1,9 @@
import type { ProjectConfig } from 'expo/config';
import { type NormalizedOptions } from './Fingerprint.types';
/**
* An out-of-process `expo/config` loader that can be used to get the Expo config and loaded modules.
*/
export declare function getExpoConfigAsync(projectRoot: string, options: NormalizedOptions): Promise<{
config: ProjectConfig | null;
loadedModules: string[] | null;
}>;
+54
View File
@@ -0,0 +1,54 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getExpoConfigAsync = getExpoConfigAsync;
const promises_1 = __importDefault(require("fs/promises"));
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
const resolve_from_1 = __importDefault(require("resolve-from"));
const ExpoConfigLoader_1 = require("./ExpoConfigLoader");
const SpawnIPC_1 = require("./utils/SpawnIPC");
/**
* An out-of-process `expo/config` loader that can be used to get the Expo config and loaded modules.
*/
async function getExpoConfigAsync(projectRoot, options) {
const result = {
config: null,
loadedModules: null,
};
if (!resolve_from_1.default.silent(path_1.default.resolve(projectRoot), 'expo/config')) {
return result;
}
const tmpDir = await promises_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'expo-fingerprint-'));
const ignoredFile = await createTempIgnoredFileAsync(tmpDir, options);
try {
const { message } = await (0, SpawnIPC_1.spawnWithIpcAsync)('node', [(0, ExpoConfigLoader_1.getExpoConfigLoaderPath)(), path_1.default.resolve(projectRoot), ignoredFile], { cwd: projectRoot });
const stdoutJson = JSON.parse(message);
result.config = stdoutJson.config;
result.loadedModules = stdoutJson.loadedModules;
}
catch (e) {
if (e instanceof Error) {
console.warn(`Cannot get Expo config from an Expo project - ${e.message}: `, e.stack);
}
}
finally {
try {
await promises_1.default.rm(tmpDir, { recursive: true });
}
catch { }
}
return result;
}
/**
* Create a temporary file with ignored paths from options that will be read by the ExpoConfigLoader.
*/
async function createTempIgnoredFileAsync(tmpDir, options) {
const ignoredFile = path_1.default.join(tmpDir, '.fingerprintignore');
const ignorePaths = options.ignorePathMatchObjects.map((match) => match.pattern);
await promises_1.default.writeFile(ignoredFile, ignorePaths.join('\n'));
return ignoredFile;
}
//# sourceMappingURL=ExpoConfig.js.map
@@ -0,0 +1 @@
{"version":3,"file":"ExpoConfig.js","sourceRoot":"","sources":["../src/ExpoConfig.ts"],"names":[],"mappings":";;;;;AAaA,gDAsCC;AAlDD,2DAA6B;AAC7B,4CAAoB;AACpB,gDAAwB;AACxB,gEAAuC;AAEvC,yDAA6D;AAE7D,+CAAqD;AAErD;;GAEG;AACI,KAAK,UAAU,kBAAkB,CACtC,WAAmB,EACnB,OAA0B;IAK1B,MAAM,MAAM,GAAG;QACb,MAAM,EAAE,IAAI;QACZ,aAAa,EAAE,IAAI;KACpB,CAAC;IAEF,IAAI,CAAC,sBAAW,CAAC,MAAM,CAAC,cAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,aAAa,CAAC,EAAE,CAAC;QAClE,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,kBAAE,CAAC,OAAO,CAAC,cAAI,CAAC,IAAI,CAAC,YAAE,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAC7E,MAAM,WAAW,GAAG,MAAM,0BAA0B,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtE,IAAI,CAAC;QACH,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAA,4BAAiB,EACzC,MAAM,EACN,CAAC,IAAA,0CAAuB,GAAE,EAAE,cAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,EACnE,EAAE,GAAG,EAAE,WAAW,EAAE,CACrB,CAAC;QACF,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;QAClC,MAAM,CAAC,aAAa,GAAG,UAAU,CAAC,aAAa,CAAC;IAClD,CAAC;IAAC,OAAO,CAAU,EAAE,CAAC;QACpB,IAAI,CAAC,YAAY,KAAK,EAAE,CAAC;YACvB,OAAO,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;QACxF,CAAC;IACH,CAAC;YAAS,CAAC;QACT,IAAI,CAAC;YACH,MAAM,kBAAE,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,0BAA0B,CACvC,MAAc,EACd,OAA0B;IAE1B,MAAM,WAAW,GAAG,cAAI,CAAC,IAAI,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAC5D,MAAM,WAAW,GAAG,OAAO,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACjF,MAAM,kBAAE,CAAC,SAAS,CAAC,WAAW,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACxD,OAAO,WAAW,CAAC;AACrB,CAAC"}
@@ -0,0 +1,7 @@
/**
* A helper script to load the Expo config and loaded plugins from a project
*/
/**
* Get the path to the ExpoConfigLoader file.
*/
export declare function getExpoConfigLoaderPath(): string;
+142
View File
@@ -0,0 +1,142 @@
"use strict";
/**
* A helper script to load the Expo config and loaded plugins from a project
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getExpoConfigLoaderPath = getExpoConfigLoaderPath;
const promises_1 = __importDefault(require("fs/promises"));
const module_1 = __importDefault(require("module"));
const node_assert_1 = __importDefault(require("node:assert"));
const node_process_1 = __importDefault(require("node:process"));
const path_1 = __importDefault(require("path"));
const resolve_from_1 = __importDefault(require("resolve-from"));
const ExpoResolver_1 = require("./ExpoResolver");
const Options_1 = require("./Options");
const Path_1 = require("./utils/Path");
async function runAsync(programName, args = []) {
if (args.length < 1) {
console.log(`Usage: ${programName} <projectRoot> [ignoredFile]`);
return;
}
const projectRoot = path_1.default.resolve(args[0]);
const ignoredFile = args[1] ? path_1.default.resolve(args[1]) : null;
// @ts-expect-error: module internal _cache
const loadedModulesBefore = new Set(Object.keys(module_1.default._cache));
const expoEnvPath = (0, ExpoResolver_1.resolveExpoEnvPath)(projectRoot);
(0, node_assert_1.default)(expoEnvPath, `Could not find '@expo/env' package for the project from ${projectRoot}.`);
require(expoEnvPath).load(projectRoot);
setNodeEnv('development');
const { getConfig } = require((0, resolve_from_1.default)(path_1.default.resolve(projectRoot), 'expo/config'));
const config = await getConfig(projectRoot, { skipSDKVersionRequirement: true });
// @ts-expect-error: module internal _cache
const loadedModules = Object.keys(module_1.default._cache)
.filter((modulePath) => !loadedModulesBefore.has(modulePath))
.map((modulePath) => path_1.default.relative(projectRoot, modulePath));
const ignoredPaths = [
...DEFAULT_CONFIG_LOADING_IGNORE_PATHS,
...(await loadIgnoredPathsAsync(ignoredFile)),
];
const filteredLoadedModules = loadedModules.filter((modulePath) => !(0, Path_1.isIgnoredPath)(modulePath, ignoredPaths));
const result = JSON.stringify({ config, loadedModules: filteredLoadedModules });
if (node_process_1.default.send) {
node_process_1.default.send(result);
}
else {
console.log(result);
}
}
// If running from the command line
if (require.main?.filename === __filename) {
(async () => {
const programIndex = node_process_1.default.argv.findIndex((arg) => arg === __filename);
try {
await runAsync(node_process_1.default.argv[programIndex], node_process_1.default.argv.slice(programIndex + 1));
}
catch (e) {
console.error('Uncaught Error', e);
node_process_1.default.exit(1);
}
})();
}
/**
* Load the generated ignored paths file from caller and remove the file after loading
*/
async function loadIgnoredPathsAsync(ignoredFile) {
if (!ignoredFile) {
return Options_1.DEFAULT_IGNORE_PATHS;
}
const ignorePaths = [];
try {
const fingerprintIgnore = await promises_1.default.readFile(ignoredFile, 'utf8');
const fingerprintIgnoreLines = fingerprintIgnore.split('\n');
for (const line of fingerprintIgnoreLines) {
const trimmedLine = line.trim();
if (trimmedLine) {
ignorePaths.push(trimmedLine);
}
}
}
catch { }
return ignorePaths;
}
/**
* Get the path to the ExpoConfigLoader file.
*/
function getExpoConfigLoaderPath() {
return path_1.default.join(__dirname, 'ExpoConfigLoader.js');
}
/**
* Set the environment to production or development
* Replicates the code from `@expo/cli` to ensure the same environment is set.
*/
function setNodeEnv(mode) {
node_process_1.default.env.NODE_ENV = node_process_1.default.env.NODE_ENV || mode;
node_process_1.default.env.BABEL_ENV = node_process_1.default.env.BABEL_ENV || node_process_1.default.env.NODE_ENV;
// @ts-expect-error: Add support for external React libraries being loaded in the same process.
globalThis.__DEV__ = node_process_1.default.env.NODE_ENV !== 'production';
}
// Ignore default javascript files when calling `getConfig()`
const DEFAULT_CONFIG_LOADING_IGNORE_PATHS = [
// We don't want to include the whole project package.json from the ExpoConfigLoader phase.
'package.json',
'**/node_modules/@babel/**/*',
'**/node_modules/@expo/**/*',
'**/node_modules/@jridgewell/**/*',
'**/node_modules/expo/config.js',
'**/node_modules/expo/config-plugins.js',
`**/node_modules/{${[
'ajv',
'ajv-formats',
'ajv-keywords',
'ansi-styles',
'chalk',
'debug',
'dotenv',
'dotenv-expand',
'escape-string-regexp',
'getenv',
'graceful-fs',
'fast-deep-equal',
'fast-uri',
'has-flag',
'imurmurhash',
'js-tokens',
'json5',
'json-schema-traverse',
'ms',
'picocolors',
'lines-and-columns',
'require-from-string',
'resolve-from',
'schema-utils',
'signal-exit',
'sucrase',
'supports-color',
'ts-interface-checker',
'write-file-atomic',
].join(',')}}/**/*`,
];
//# sourceMappingURL=ExpoConfigLoader.js.map
@@ -0,0 +1 @@
{"version":3,"file":"ExpoConfigLoader.js","sourceRoot":"","sources":["../src/ExpoConfigLoader.ts"],"names":[],"mappings":"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"}
+30
View File
@@ -0,0 +1,30 @@
/**
* Resolve the version of `expo` package in the project.
*/
export declare function resolveExpoVersion(projectRoot: string): string | null;
/**
* Resolve the path to the `@expo/env` package in the project.
*/
export declare function resolveExpoEnvPath(projectRoot: string): string | null;
/**
* Resolve the package root of `expo-modules-autolinking` package in the project.
*/
export declare function resolveExpoAutolinkingPackageRoot(projectRoot: string): string | null;
/**
* Resolve the path to the `expo-modules-autolinking` CLI in the project.
* @throws If the package is not found in the project.
*/
export declare function resolveExpoAutolinkingCliPath(projectRoot: string): string;
/**
* Resolve the version of `expo-modules-autolinking` package in the project.
*/
export declare function resolveExpoAutolinkingVersion(projectRoot: string): string | null;
/**
* Resolve the package root of `expo/config-plugins` package in the project.
*/
export declare function resolveExpoConfigPluginsPackagePath(projectRoot: string): string | null;
/**
* Resolve the `expo` package version and check if it satisfies the provided semver range.
* @returns `null` if the `expo` package is not found in the project.
*/
export declare function satisfyExpoVersion(projectRoot: string, range: string): boolean | null;
+97
View File
@@ -0,0 +1,97 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveExpoVersion = resolveExpoVersion;
exports.resolveExpoEnvPath = resolveExpoEnvPath;
exports.resolveExpoAutolinkingPackageRoot = resolveExpoAutolinkingPackageRoot;
exports.resolveExpoAutolinkingCliPath = resolveExpoAutolinkingCliPath;
exports.resolveExpoAutolinkingVersion = resolveExpoAutolinkingVersion;
exports.resolveExpoConfigPluginsPackagePath = resolveExpoConfigPluginsPackagePath;
exports.satisfyExpoVersion = satisfyExpoVersion;
const path_1 = __importDefault(require("path"));
const resolve_from_1 = __importDefault(require("resolve-from"));
const semver_1 = __importDefault(require("semver"));
let cachedExpoAutolinkingPackageRoot = null;
/**
* Resolve the version of `expo` package in the project.
*/
function resolveExpoVersion(projectRoot) {
const expoPackageJsonPath = resolve_from_1.default.silent(projectRoot, 'expo/package.json');
if (expoPackageJsonPath) {
const expoPackageJson = require(expoPackageJsonPath);
return expoPackageJson.version;
}
return null;
}
/**
* Resolve the path to the `@expo/env` package in the project.
*/
function resolveExpoEnvPath(projectRoot) {
const expoPackageRoot = resolve_from_1.default.silent(projectRoot, 'expo/package.json');
const expoEnvPackageJsonPath = resolve_from_1.default.silent(expoPackageRoot ?? projectRoot, '@expo/env/package.json');
if (expoEnvPackageJsonPath) {
return path_1.default.dirname(expoEnvPackageJsonPath);
}
return null;
}
/**
* Resolve the package root of `expo-modules-autolinking` package in the project.
*/
function resolveExpoAutolinkingPackageRoot(projectRoot) {
if (cachedExpoAutolinkingPackageRoot) {
const [cachedProjectRoot, cachedPackageRoot] = cachedExpoAutolinkingPackageRoot;
if (cachedProjectRoot === projectRoot) {
return cachedPackageRoot;
}
}
const expoPackageRoot = resolve_from_1.default.silent(projectRoot, 'expo/package.json');
const autolinkingPackageJsonPath = resolve_from_1.default.silent(expoPackageRoot ?? projectRoot, 'expo-modules-autolinking/package.json');
if (autolinkingPackageJsonPath) {
const autolinkingPackageRoot = path_1.default.dirname(autolinkingPackageJsonPath);
cachedExpoAutolinkingPackageRoot = [projectRoot, autolinkingPackageRoot];
return autolinkingPackageRoot;
}
return null;
}
/**
* Resolve the path to the `expo-modules-autolinking` CLI in the project.
* @throws If the package is not found in the project.
*/
function resolveExpoAutolinkingCliPath(projectRoot) {
const autolinkingPackageRoot = resolveExpoAutolinkingPackageRoot(projectRoot);
if (autolinkingPackageRoot == null) {
throw new Error('Cannot resolve expo-modules-autolinking package in the project.');
}
return path_1.default.join(autolinkingPackageRoot, 'bin', 'expo-modules-autolinking.js');
}
/**
* Resolve the version of `expo-modules-autolinking` package in the project.
*/
function resolveExpoAutolinkingVersion(projectRoot) {
const autolinkingPackageRoot = resolveExpoAutolinkingPackageRoot(projectRoot);
if (autolinkingPackageRoot) {
const autolinkingPackageJson = require(path_1.default.join(autolinkingPackageRoot, 'package.json'));
return autolinkingPackageJson.version;
}
return null;
}
/**
* Resolve the package root of `expo/config-plugins` package in the project.
*/
function resolveExpoConfigPluginsPackagePath(projectRoot) {
return resolve_from_1.default.silent(projectRoot, 'expo/config-plugins') ?? null;
}
/**
* Resolve the `expo` package version and check if it satisfies the provided semver range.
* @returns `null` if the `expo` package is not found in the project.
*/
function satisfyExpoVersion(projectRoot, range) {
const expoVersion = resolveExpoVersion(projectRoot);
if (expoVersion) {
return semver_1.default.satisfies(expoVersion, range);
}
return null;
}
//# sourceMappingURL=ExpoResolver.js.map
@@ -0,0 +1 @@
{"version":3,"file":"ExpoResolver.js","sourceRoot":"","sources":["../src/ExpoResolver.ts"],"names":[],"mappings":"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"}
+53
View File
@@ -0,0 +1,53 @@
import type { Fingerprint, FingerprintDiffItem, Options } from './Fingerprint.types';
/**
* 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 for a project.
*
* @example
* ```ts
* const hash = await createProjectHashAsync('/app');
* console.log(hash);
* ```
*/
export declare function createProjectHashAsync(projectRoot: string, options?: Options): Promise<string>;
/**
* 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[]>;
/**
* 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[];
+112
View File
@@ -0,0 +1,112 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createFingerprintAsync = createFingerprintAsync;
exports.createProjectHashAsync = createProjectHashAsync;
exports.diffFingerprintChangesAsync = diffFingerprintChangesAsync;
exports.diffFingerprints = diffFingerprints;
const Dedup_1 = require("./Dedup");
const Options_1 = require("./Options");
const Sort_1 = require("./Sort");
const Hash_1 = require("./hash/Hash");
const Sourcer_1 = require("./sourcer/Sourcer");
/**
* Create a fingerprint for a project.
* @example
* ```js
* const fingerprint = await createFingerprintAsync('/app');
* console.log(fingerprint);
* ```
*/
async function createFingerprintAsync(projectRoot, options) {
const opts = await (0, Options_1.normalizeOptionsAsync)(projectRoot, options);
const sources = await (0, Sourcer_1.getHashSourcesAsync)(projectRoot, opts);
const normalizedSources = (0, Sort_1.sortSources)((0, Dedup_1.dedupSources)(sources, projectRoot));
const fingerprint = await (0, Hash_1.createFingerprintFromSourcesAsync)(normalizedSources, projectRoot, opts);
return fingerprint;
}
/**
* Create a native hash value for a project.
*
* @example
* ```ts
* const hash = await createProjectHashAsync('/app');
* console.log(hash);
* ```
*/
async function createProjectHashAsync(projectRoot, options) {
const fingerprint = await createFingerprintAsync(projectRoot, options);
return fingerprint.hash;
}
/**
* 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);
* ```
*/
async function diffFingerprintChangesAsync(fingerprint, projectRoot, options) {
const newFingerprint = await createFingerprintAsync(projectRoot, options);
if (fingerprint.hash === newFingerprint.hash) {
return [];
}
return diffFingerprints(fingerprint, newFingerprint);
}
/**
* 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);
* ```
*/
function diffFingerprints(fingerprint1, fingerprint2) {
let index1 = 0;
let index2 = 0;
const diff = [];
while (index1 < fingerprint1.sources.length && index2 < fingerprint2.sources.length) {
const source1 = fingerprint1.sources[index1];
const source2 = fingerprint2.sources[index2];
const compareResult = (0, Sort_1.compareSource)(source1, source2);
if (compareResult === 0) {
if (source1.hash !== source2.hash) {
diff.push({ op: 'changed', beforeSource: source1, afterSource: source2 });
}
++index1;
++index2;
}
else if (compareResult < 0) {
diff.push({ op: 'removed', removedSource: source1 });
++index1;
}
else {
diff.push({ op: 'added', addedSource: source2 });
++index2;
}
}
while (index1 < fingerprint1.sources.length) {
diff.push({ op: 'removed', removedSource: fingerprint1.sources[index1] });
++index1;
}
while (index2 < fingerprint2.sources.length) {
diff.push({ op: 'added', addedSource: fingerprint2.sources[index2] });
++index2;
}
return diff;
}
//# sourceMappingURL=Fingerprint.js.map
@@ -0,0 +1 @@
{"version":3,"file":"Fingerprint.js","sourceRoot":"","sources":["../src/Fingerprint.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,255 @@
import type { Minimatch } from 'minimatch';
import type { SourceSkips } from './sourcer/SourceSkips';
export type FingerprintSource = HashSource & {
/**
* Hash value of the `source`.
* If the source is excluded the value will be null.
*/
hash: string | null;
/**
* Debug info from the hashing process. Differs based on source type. Designed to be consumed by humans
* as opposed to programmatically.
*/
debugInfo?: DebugInfo;
};
export interface Fingerprint {
/**
* Sources and their hash values from which the project fingerprint was generated.
*/
sources: FingerprintSource[];
/**
* The final hash value of the whole project fingerprint.
*/
hash: string;
}
export type FingerprintDiffItem = {
/**
* The operation type of the diff item.
*/
op: 'added';
/**
* The added source.
*/
addedSource: FingerprintSource;
} | {
/**
* The operation type of the diff item.
*/
op: 'removed';
/**
* The removed source.
*/
removedSource: FingerprintSource;
} | {
/**
* The operation type of the diff item.
*/
op: 'changed';
/**
* The source before.
*/
beforeSource: FingerprintSource;
/**
* The source after.
*/
afterSource: FingerprintSource;
};
export type Platform = 'android' | 'ios';
export type ProjectWorkflow = 'generic' | 'managed' | 'unknown';
export interface Options {
/**
* Limit native files to those for specified platforms.
* @default ['android', 'ios']
*/
platforms?: Platform[];
/**
* I/O concurrency limit.
* @default The number of CPU cores.
*/
concurrentIoLimit?: number;
/**
* The algorithm to use for `crypto.createHash()`.
* @default 'sha1'
*/
hashAlgorithm?: string;
/**
* 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']`.
* @deprecated Use `ignorePaths` instead.
*/
dirExcludes?: string[];
/**
* Ignore files and directories from hashing. The supported pattern is the same as `glob()`.
*
* 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.
*
* Fingerprint comes with implicit default ignorePaths defined in `Options.DEFAULT_IGNORE_PATHS`.
* If you want to override the default ignorePaths, use `!` prefix in `ignorePaths`.
*/
ignorePaths?: string[];
/**
* Additional sources for hashing.
*/
extraSources?: HashSource[];
/**
* Skips some sources from fingerprint. Value is the result of bitwise-OR'ing desired values of SourceSkips.
* @default DEFAULT_SOURCE_SKIPS
*/
sourceSkips?: SourceSkips;
/**
* Enable ReactImportsPatcher to transform imports from React of the form `#import "RCTBridge.h"` to `#import <React/RCTBridge.h>`.
* This is useful when you want to have a stable fingerprint for Expo projects,
* since expo-modules-autolinking will change the import style on iOS.
* @default true for Expo SDK 51 and lower.
*/
enableReactImportsPatcher?: boolean;
/**
* 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.
*/
useRNCoreAutolinkingFromExpo?: boolean;
/**
* Whether running the functions should mute all console output. This is useful when fingerprinting is being done as
* part of a CLI that outputs a fingerprint and outputting anything else pollutes the results.
*/
silent?: boolean;
/**
* Whether to include verbose debug info in source output. Useful for debugging.
*/
debug?: boolean;
/**
* A custom hook function to transform file content sources before hashing.
*/
fileHookTransform?: FileHookTransformFunction;
}
type SourceSkipsKeys = keyof typeof SourceSkips;
/**
* Supported options for use in fingerprint.config.js
*/
export type Config = Pick<Options, 'concurrentIoLimit' | 'hashAlgorithm' | 'ignorePaths' | 'extraSources' | 'enableReactImportsPatcher' | 'useRNCoreAutolinkingFromExpo' | 'debug' | 'fileHookTransform'> & {
sourceSkips?: SourceSkips | SourceSkipsKeys[];
};
/**
* Hook function to transform file content sources before hashing.
*/
export type FileHookTransformFunction = (
/**
* Source from HashSourceFile or HashSourceContents.
*/
source: FileHookTransformSource,
/**
* The chunk of file content.
* When the stream reaches the end, the chunk will be null.
*/
chunk: Buffer | string | null,
/**
* Indicates the end of the file.
*/
isEndOfFile: boolean,
/**
* The encoding of the chunk.
*/
encoding: BufferEncoding) => Buffer | string | null;
/**
* The `source` parameter for `FileHookTransformFunction`.
*/
export type FileHookTransformSource = {
type: 'file';
filePath: string;
} | {
type: 'contents';
id: string;
};
export interface HashSourceFile {
type: 'file';
filePath: string;
/**
* Reasons of this source coming from.
*/
reasons: string[];
/**
* Override key for hashing.
* Without this key, the `filePath` is used as the hash key.
*/
overrideHashKey?: string;
}
export interface HashSourceDir {
type: 'dir';
filePath: string;
/**
* Reasons of this source coming from.
*/
reasons: string[];
/**
* Override key for hashing.
* Without this key, the `filePath` is used as the hash key.
*/
overrideHashKey?: string;
}
export interface HashSourceContents {
type: 'contents';
id: string;
contents: string | Buffer;
/**
* Reasons of this source coming from.
*/
reasons: string[];
}
export type HashSource = HashSourceFile | HashSourceDir | HashSourceContents;
export interface DebugInfoFile {
path: string;
hash: string;
/** Indicates whether the source is transformed by `fileHookTransform`. */
isTransformed?: boolean;
}
export interface DebugInfoDir {
path: string;
hash: string;
children: (DebugInfoFile | DebugInfoDir | undefined)[];
}
export interface DebugInfoContents {
hash: string;
/** Indicates whether the source is transformed by `fileHookTransform`. */
isTransformed?: boolean;
}
export type DebugInfo = DebugInfoFile | DebugInfoDir | DebugInfoContents;
export interface HashResultFile {
type: 'file';
id: string;
hex: string;
debugInfo?: DebugInfoFile;
}
export interface HashResultDir {
type: 'dir';
id: string;
hex: string;
debugInfo?: DebugInfoDir;
}
export interface HashResultContents {
type: 'contents';
id: string;
hex: string;
debugInfo?: DebugInfoContents;
}
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: Minimatch[];
/**
* A ignore pattern list specific for dir matching. It is built by `ignorePathMatchObjects` in runtime.
*/
ignoreDirMatchObjects: Minimatch[];
/**
* Indicate whether the project is using CNG for each platform.
*/
useCNGForPlatforms: Record<Platform, boolean>;
};
export {};
@@ -0,0 +1,4 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//#endregion
//# sourceMappingURL=Fingerprint.types.js.map
@@ -0,0 +1 @@
{"version":3,"file":"Fingerprint.types.js","sourceRoot":"","sources":["../src/Fingerprint.types.ts"],"names":[],"mappings":";;AA+TA,YAAY"}
+6
View File
@@ -0,0 +1,6 @@
import type { NormalizedOptions, Options } from './Fingerprint.types';
import { SourceSkips } from './sourcer/SourceSkips';
export declare const FINGERPRINT_IGNORE_FILENAME = ".fingerprintignore";
export declare const DEFAULT_IGNORE_PATHS: string[];
export declare const DEFAULT_SOURCE_SKIPS = SourceSkips.PackageJsonAndroidAndIosScriptsIfNotContainRun;
export declare function normalizeOptionsAsync(projectRoot: string, options?: Options): Promise<NormalizedOptions>;
+127
View File
@@ -0,0 +1,127 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_SOURCE_SKIPS = exports.DEFAULT_IGNORE_PATHS = exports.FINGERPRINT_IGNORE_FILENAME = void 0;
exports.normalizeOptionsAsync = normalizeOptionsAsync;
const promises_1 = __importDefault(require("fs/promises"));
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
const Config_1 = require("./Config");
const ExpoResolver_1 = require("./ExpoResolver");
const ProjectWorkflow_1 = require("./ProjectWorkflow");
const SourceSkips_1 = require("./sourcer/SourceSkips");
const Path_1 = require("./utils/Path");
exports.FINGERPRINT_IGNORE_FILENAME = '.fingerprintignore';
exports.DEFAULT_IGNORE_PATHS = [
exports.FINGERPRINT_IGNORE_FILENAME,
// Android
'**/android/build/**/*',
'**/android/.cxx/**/*',
'**/android/.gradle/**/*',
'**/android/app/build/**/*',
'**/android/app/.cxx/**/*',
'**/android/app/.gradle/**/*',
'**/android-annotation/build/**/*',
'**/android-annotation/.cxx/**/*',
'**/android-annotation/.gradle/**/*',
'**/android-annotation-processor/build/**/*',
'**/android-annotation-processor/.cxx/**/*',
'**/android-annotation-processor/.gradle/**/*',
// Often has different line endings, thus we have to ignore it
'**/android/gradlew.bat',
// Android gradle plugins
'**/*-gradle-plugin/build/**/*',
'**/*-gradle-plugin/.cxx/**/*',
'**/*-gradle-plugin/.gradle/**/*',
// iOS
'**/ios/Pods/**/*',
'**/ios/build/**/*',
'**/ios/.xcode.env.local',
'**/ios/**/project.xcworkspace',
'**/ios/*.xcworkspace/xcuserdata/**/*',
// System files that differ from machine to machine
'**/.DS_Store',
// Ignore all expo configs because we will read expo config in a HashSourceContents already
'app.config.ts',
'app.config.js',
'app.config.json',
'app.json',
// Ignore CocoaPods generated files
// https://github.com/expo/expo/blob/d0e39858ead9a194d90990f89903e773b9d33582/packages/expo-sqlite/ios/ExpoSQLite.podspec#L25-L36
// https://github.com/expo/expo/blob/d0e39858ead9a194d90990f89903e773b9d33582/packages/expo-updates/ios/EXUpdates.podspec#L51-L58
'**/node_modules/expo-sqlite/ios/sqlite3.[ch]',
'**/node_modules/expo-updates/ios/EXUpdates/BSPatch/bspatch.c',
// Ignore nested node_modules
'**/node_modules/**/node_modules/**',
// Ignore node binaries that might be platform dependent
'**/node_modules/**/*.node',
'**/node_modules/@img/sharp-*/**/*',
'**/node_modules/sharp/{build,vendor}/**/*',
];
exports.DEFAULT_SOURCE_SKIPS = SourceSkips_1.SourceSkips.PackageJsonAndroidAndIosScriptsIfNotContainRun;
async function normalizeOptionsAsync(projectRoot, options) {
const config = await (0, Config_1.loadConfigAsync)(projectRoot, options?.silent ?? false);
const ignorePathMatchObjects = await collectIgnorePathsAsync(projectRoot, config?.ignorePaths, options);
const useCNGForPlatforms = await resolveUseCNGAsync(projectRoot, options, ignorePathMatchObjects);
if (useCNGForPlatforms.android) {
(0, Path_1.appendIgnorePath)(ignorePathMatchObjects, 'android/**/*');
}
if (useCNGForPlatforms.ios) {
(0, Path_1.appendIgnorePath)(ignorePathMatchObjects, 'ios/**/*');
}
return {
// Defaults
platforms: ['android', 'ios'],
concurrentIoLimit: os_1.default.cpus().length,
hashAlgorithm: 'sha1',
sourceSkips: exports.DEFAULT_SOURCE_SKIPS,
// Options from config
...config,
// Explicit options
...Object.fromEntries(Object.entries(options ?? {}).filter(([_, v]) => v != null)),
// These options are computed by both default and explicit options, so we put them last.
enableReactImportsPatcher: options?.enableReactImportsPatcher ??
config?.enableReactImportsPatcher ??
(0, ExpoResolver_1.satisfyExpoVersion)(projectRoot, '<52.0.0') ??
false,
ignorePathMatchObjects,
ignoreDirMatchObjects: (0, Path_1.buildDirMatchObjects)(ignorePathMatchObjects),
useCNGForPlatforms,
};
}
async function collectIgnorePathsAsync(projectRoot, pathsFromConfig, options) {
const ignorePaths = [
...exports.DEFAULT_IGNORE_PATHS,
...(pathsFromConfig ?? []),
...(options?.ignorePaths ?? []),
...(options?.dirExcludes?.map((dirExclude) => `${dirExclude}/**/*`) ?? []),
];
const fingerprintIgnorePath = path_1.default.join(projectRoot, exports.FINGERPRINT_IGNORE_FILENAME);
try {
const fingerprintIgnore = await promises_1.default.readFile(fingerprintIgnorePath, 'utf8');
const fingerprintIgnoreLines = fingerprintIgnore.split('\n');
for (const line of fingerprintIgnoreLines) {
const trimmedLine = line.trim();
if (trimmedLine) {
ignorePaths.push(trimmedLine);
}
}
}
catch { }
return (0, Path_1.buildPathMatchObjects)(ignorePaths);
}
async function resolveUseCNGAsync(projectRoot, options, ignorePathMatchObjects) {
const results = {
android: false,
ios: false,
};
const platforms = options?.platforms ?? ['android', 'ios'];
for (const platform of platforms) {
const projectWorkflow = await (0, ProjectWorkflow_1.resolveProjectWorkflowAsync)(projectRoot, platform, ignorePathMatchObjects);
results[platform] = projectWorkflow === 'managed';
}
return results;
}
//# sourceMappingURL=Options.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Options.js","sourceRoot":"","sources":["../src/Options.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
import { type Minimatch } from 'minimatch';
import { type Platform, type ProjectWorkflow } from './Fingerprint.types';
/**
* Replicated project workflow detection logic from expo-updates:
* - https://github.com/expo/expo/blob/9b829e0749b8ff04f55a02b03cd1fefa74c5cd8d/packages/expo-updates/utils/src/workflow.ts
* - https://github.com/expo/expo/blob/9b829e0749b8ff04f55a02b03cd1fefa74c5cd8d/packages/expo-updates/utils/src/vcs.ts
*/
export declare function resolveProjectWorkflowAsync(projectRoot: string, platform: Platform, fingerprintIgnorePaths: Minimatch[]): Promise<ProjectWorkflow>;
export declare function resolveProjectWorkflowPerPlatformAsync(projectRoot: string, fingerprintIgnorePaths: Minimatch[]): Promise<Record<Platform, ProjectWorkflow>>;
+163
View File
@@ -0,0 +1,163 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveProjectWorkflowAsync = resolveProjectWorkflowAsync;
exports.resolveProjectWorkflowPerPlatformAsync = resolveProjectWorkflowPerPlatformAsync;
const spawn_async_1 = __importDefault(require("@expo/spawn-async"));
const promises_1 = __importDefault(require("fs/promises"));
const glob_1 = require("glob");
const ignore_1 = __importDefault(require("ignore"));
const path_1 = __importDefault(require("path"));
const ExpoResolver_1 = require("./ExpoResolver");
const Path_1 = require("./utils/Path");
/**
* Replicated project workflow detection logic from expo-updates:
* - https://github.com/expo/expo/blob/9b829e0749b8ff04f55a02b03cd1fefa74c5cd8d/packages/expo-updates/utils/src/workflow.ts
* - https://github.com/expo/expo/blob/9b829e0749b8ff04f55a02b03cd1fefa74c5cd8d/packages/expo-updates/utils/src/vcs.ts
*/
async function resolveProjectWorkflowAsync(projectRoot, platform, fingerprintIgnorePaths) {
const configPluginsPackageRoot = (0, ExpoResolver_1.resolveExpoConfigPluginsPackagePath)(projectRoot);
if (configPluginsPackageRoot == null) {
return 'unknown';
}
const { AndroidConfig, IOSConfig } = require(configPluginsPackageRoot);
let platformWorkflowMarkers;
try {
platformWorkflowMarkers =
platform === 'android'
? [
path_1.default.join(projectRoot, 'android/app/build.gradle'),
await AndroidConfig.Paths.getAndroidManifestAsync(projectRoot),
]
: [IOSConfig.Paths.getPBXProjectPath(projectRoot)];
}
catch {
return 'managed';
}
const vcsClient = await getVCSClientAsync(projectRoot);
const vcsRoot = path_1.default.normalize(await vcsClient.getRootPathAsync());
for (const marker of platformWorkflowMarkers) {
const relativeMarker = path_1.default.relative(vcsRoot, marker);
if ((await (0, Path_1.pathExistsAsync)(marker)) &&
!(0, Path_1.isIgnoredPathWithMatchObjects)(relativeMarker, fingerprintIgnorePaths) &&
!(await vcsClient.isFileIgnoredAsync(relativeMarker))) {
return 'generic';
}
}
return 'managed';
}
async function resolveProjectWorkflowPerPlatformAsync(projectRoot, fingerprintIgnorePaths) {
const [android, ios] = await Promise.all([
resolveProjectWorkflowAsync(projectRoot, 'android', fingerprintIgnorePaths),
resolveProjectWorkflowAsync(projectRoot, 'ios', fingerprintIgnorePaths),
]);
return { android, ios };
}
async function getVCSClientAsync(projectRoot) {
if (await isGitInstalledAndConfiguredAsync()) {
return new GitClient();
}
else {
return new NoVCSClient(projectRoot);
}
}
class GitClient {
async getRootPathAsync() {
return (await (0, spawn_async_1.default)('git', ['rev-parse', '--show-toplevel'])).stdout.trim();
}
async isFileIgnoredAsync(filePath) {
try {
await (0, spawn_async_1.default)('git', ['check-ignore', '-q', filePath], {
cwd: path_1.default.normalize(await this.getRootPathAsync()),
});
return true;
}
catch {
return false;
}
}
}
class NoVCSClient {
projectRoot;
constructor(projectRoot) {
this.projectRoot = projectRoot;
}
async getRootPathAsync() {
return this.projectRoot;
}
async isFileIgnoredAsync(filePath) {
const ignore = new Ignore(this.projectRoot);
await ignore.initIgnoreAsync();
return ignore.ignores(filePath);
}
}
async function isGitInstalledAndConfiguredAsync() {
try {
await (0, spawn_async_1.default)('git', ['--help']);
}
catch (error) {
if (error.code === 'ENOENT') {
return false;
}
throw error;
}
try {
await (0, spawn_async_1.default)('git', ['rev-parse', '--show-toplevel']);
}
catch {
return false;
}
return true;
}
const GITIGNORE_FILENAME = '.gitignore';
const DEFAULT_IGNORE = `
.git
node_modules
`;
/**
* Ignore wraps the 'ignore' package to support multiple .gitignore files
* in subdirectories.
*
* Inconsistencies with git behavior:
* - if parent .gitignore has ignore rule and child has exception to that rule,
* file will still be ignored,
* - node_modules is always ignored
*
* Differs from the eas-cli Ignore class by not using `.easignore`. Otherwise this is copied. May try
* to merge the implementations soon.
*/
class Ignore {
rootDir;
ignoreMapping = [];
constructor(rootDir) {
this.rootDir = rootDir;
}
async initIgnoreAsync() {
const ignoreFilePaths = (await (0, glob_1.glob)(`**/${GITIGNORE_FILENAME}`, {
cwd: this.rootDir,
ignore: ['node_modules'],
follow: false,
}))
// ensure that parent dir is before child directories
.sort((a, b) => a.length - b.length && a.localeCompare(b));
const ignoreMapping = await Promise.all(ignoreFilePaths.map(async (filePath) => {
return [
filePath.slice(0, filePath.length - GITIGNORE_FILENAME.length),
(0, ignore_1.default)().add(await promises_1.default.readFile(path_1.default.join(this.rootDir, filePath), 'utf-8')),
];
}));
this.ignoreMapping = [['', (0, ignore_1.default)().add(DEFAULT_IGNORE)], ...ignoreMapping];
}
ignores(relativePath) {
for (const [prefix, ignore] of this.ignoreMapping) {
if (relativePath.startsWith(prefix) && ignore.ignores(relativePath.slice(prefix.length))) {
return true;
}
}
return false;
}
}
//#endregion - a copy of vcs client and ignore handler from expo-updates
//# sourceMappingURL=ProjectWorkflow.js.map
@@ -0,0 +1 @@
{"version":3,"file":"ProjectWorkflow.js","sourceRoot":"","sources":["../src/ProjectWorkflow.ts"],"names":[],"mappings":"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"}
+11
View File
@@ -0,0 +1,11 @@
import type { HashSource } from './Fingerprint.types';
export declare function sortSources<T extends HashSource>(sources: T[]): T[];
/**
* Comparator between two sources.
* This is useful for sorting sources in a consistent order.
* @returns:
* == 0 if a and b are equal,
* < 0 if a is less than b,
* > 0 if a is greater than b.
*/
export declare function compareSource(a: HashSource, b: HashSource): number;
+40
View File
@@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.sortSources = sortSources;
exports.compareSource = compareSource;
function sortSources(sources) {
return sources.sort(compareSource);
}
const typeOrder = {
file: 0,
dir: 1,
contents: 2,
};
/**
* Comparator between two sources.
* This is useful for sorting sources in a consistent order.
* @returns:
* == 0 if a and b are equal,
* < 0 if a is less than b,
* > 0 if a is greater than b.
*/
function compareSource(a, b) {
const typeResult = typeOrder[a.type] - typeOrder[b.type];
if (typeResult === 0) {
if (a.type === 'file' && b.type === 'file') {
const aValue = a.overrideHashKey ?? a.filePath;
const bValue = b.overrideHashKey ?? b.filePath;
return aValue.localeCompare(bValue);
}
else if (a.type === 'dir' && b.type === 'dir') {
const aValue = a.overrideHashKey ?? a.filePath;
const bValue = b.overrideHashKey ?? b.filePath;
return aValue.localeCompare(bValue);
}
else if (a.type === 'contents' && b.type === 'contents') {
return a.id.localeCompare(b.id);
}
}
return typeResult;
}
//# sourceMappingURL=Sort.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Sort.js","sourceRoot":"","sources":["../src/Sort.ts"],"names":[],"mappings":";;AAEA,kCAEC;AAgBD,sCAgBC;AAlCD,SAAgB,WAAW,CAAuB,OAAY;IAC5D,OAAO,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,SAAS,GAAG;IAChB,IAAI,EAAE,CAAC;IACP,GAAG,EAAE,CAAC;IACN,QAAQ,EAAE,CAAC;CACZ,CAAC;AAEF;;;;;;;GAOG;AACH,SAAgB,aAAa,CAAC,CAAa,EAAE,CAAa;IACxD,MAAM,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACzD,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;QACrB,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC3C,MAAM,MAAM,GAAG,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC,QAAQ,CAAC;YAC/C,MAAM,MAAM,GAAG,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC,QAAQ,CAAC;YAC/C,OAAO,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QACtC,CAAC;aAAM,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YAChD,MAAM,MAAM,GAAG,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC,QAAQ,CAAC;YAC/C,MAAM,MAAM,GAAG,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC,QAAQ,CAAC;YAC/C,OAAO,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QACtC,CAAC;aAAM,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC1D,OAAO,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC"}
@@ -0,0 +1,19 @@
import { Transform, type TransformCallback } from 'stream';
import type { FileHookTransformSource, FileHookTransformFunction } from '../Fingerprint.types';
/**
* A transform stream that allows to hook into file contents and transform them.
*/
export declare class FileHookTransform extends Transform {
private readonly source;
private readonly transformFn;
private readonly debug;
private _isTransformed;
constructor(source: FileHookTransformSource, transformFn: FileHookTransformFunction, debug: boolean | undefined);
/**
* Indicates whether the file content has been transformed.
* @returns boolean value if `debug` is true, otherwise the value would be undefined.
*/
get isTransformed(): boolean | undefined;
_transform(chunk: any, encoding: BufferEncoding, callback: TransformCallback): void;
_flush(callback: TransformCallback): void;
}
@@ -0,0 +1,46 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileHookTransform = void 0;
const stream_1 = require("stream");
/**
* A transform stream that allows to hook into file contents and transform them.
*/
class FileHookTransform extends stream_1.Transform {
source;
transformFn;
debug;
_isTransformed = undefined;
constructor(source, transformFn, debug) {
super();
this.source = source;
this.transformFn = transformFn;
this.debug = debug;
}
/**
* Indicates whether the file content has been transformed.
* @returns boolean value if `debug` is true, otherwise the value would be undefined.
*/
get isTransformed() {
return this._isTransformed;
}
//#region - Transform implementations
_transform(chunk, encoding, callback) {
const result = this.transformFn(this.source, chunk, false /* isEndOfFile */, encoding);
if (this.debug) {
this._isTransformed ||= chunk !== result;
}
if (result) {
this.push(result);
}
callback();
}
_flush(callback) {
const result = this.transformFn(this.source, null, true /* isEndOfFile */, 'utf8');
if (result) {
this.push(result);
}
callback();
}
}
exports.FileHookTransform = FileHookTransform;
//# sourceMappingURL=FileHookTransform.js.map
@@ -0,0 +1 @@
{"version":3,"file":"FileHookTransform.js","sourceRoot":"","sources":["../../src/hash/FileHookTransform.ts"],"names":[],"mappings":";;;AAAA,mCAA2D;AAI3D;;GAEG;AACH,MAAa,iBAAkB,SAAQ,kBAAS;IAI3B;IACA;IACA;IALX,cAAc,GAAwB,SAAS,CAAC;IAExD,YACmB,MAA+B,EAC/B,WAAsC,EACtC,KAA0B;QAE3C,KAAK,EAAE,CAAC;QAJS,WAAM,GAAN,MAAM,CAAyB;QAC/B,gBAAW,GAAX,WAAW,CAA2B;QACtC,UAAK,GAAL,KAAK,CAAqB;IAG7C,CAAC;IAED;;;OAGG;IACH,IAAI,aAAa;QACf,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;IAED,qCAAqC;IAErC,UAAU,CAAC,KAAU,EAAE,QAAwB,EAAE,QAA2B;QAC1E,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAC;QACvF,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,cAAc,KAAK,KAAK,KAAK,MAAM,CAAC;QAC3C,CAAC;QACD,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpB,CAAC;QACD,QAAQ,EAAE,CAAC;IACb,CAAC;IAED,MAAM,CAAC,QAA2B;QAChC,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;QACnF,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpB,CAAC;QACD,QAAQ,EAAE,CAAC;IACb,CAAC;CAGF;AAzCD,8CAyCC"}
+28
View File
@@ -0,0 +1,28 @@
import pLimit from 'p-limit';
import type { Fingerprint, FingerprintSource, HashResultContents, HashResultDir, HashResultFile, HashSource, HashSourceContents, NormalizedOptions } from '../Fingerprint.types';
/**
* Create a `Fingerprint` from `HashSources` array
*/
export declare function createFingerprintFromSourcesAsync(sources: HashSource[], projectRoot: string, options: NormalizedOptions): Promise<Fingerprint>;
/**
* Create a `FingerprintSource` from a `HashSource`
* This function will get a hash value and merge back to original source
*/
export declare function createFingerprintSourceAsync(source: HashSource, limiter: pLimit.Limit, projectRoot: string, options: NormalizedOptions): Promise<FingerprintSource>;
/**
* Create a `HashResult` from a file
*/
export declare function createFileHashResultsAsync(filePath: string, limiter: pLimit.Limit, projectRoot: string, options: NormalizedOptions): Promise<HashResultFile | null>;
/**
* Create `HashResult` for a dir.
* If the dir is excluded, returns null rather than a HashResult
*/
export declare function createDirHashResultsAsync(dirPath: string, limiter: pLimit.Limit, projectRoot: string, options: NormalizedOptions, depth?: number): Promise<HashResultDir | null>;
/**
* Create `HashResult` for a `HashSourceContents`
*/
export declare function createContentsHashResultsAsync(source: HashSourceContents, options: NormalizedOptions): Promise<HashResultContents>;
/**
* Create id from given source
*/
export declare function createSourceId(source: HashSource): string;
+233
View File
@@ -0,0 +1,233 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createFingerprintFromSourcesAsync = createFingerprintFromSourcesAsync;
exports.createFingerprintSourceAsync = createFingerprintSourceAsync;
exports.createFileHashResultsAsync = createFileHashResultsAsync;
exports.createDirHashResultsAsync = createDirHashResultsAsync;
exports.createContentsHashResultsAsync = createContentsHashResultsAsync;
exports.createSourceId = createSourceId;
const crypto_1 = require("crypto");
const fs_1 = require("fs");
const promises_1 = __importDefault(require("fs/promises"));
const p_limit_1 = __importDefault(require("p-limit"));
const path_1 = __importDefault(require("path"));
const stream_1 = require("stream");
const FileHookTransform_1 = require("./FileHookTransform");
const ReactImportsPatcher_1 = require("./ReactImportsPatcher");
const Path_1 = require("../utils/Path");
const Predicates_1 = require("../utils/Predicates");
const Profile_1 = require("../utils/Profile");
/**
* Create a `Fingerprint` from `HashSources` array
*/
async function createFingerprintFromSourcesAsync(sources, projectRoot, options) {
const limiter = (0, p_limit_1.default)(options.concurrentIoLimit);
const fingerprintSources = await Promise.all(sources.map((source) => createFingerprintSourceAsync(source, limiter, projectRoot, options)));
const hasher = (0, crypto_1.createHash)(options.hashAlgorithm);
for (const source of fingerprintSources) {
if (source.hash != null) {
hasher.update(createSourceId(source));
hasher.update(source.hash);
}
}
const hash = hasher.digest('hex');
return {
sources: fingerprintSources,
hash,
};
}
/**
* Create a `FingerprintSource` from a `HashSource`
* This function will get a hash value and merge back to original source
*/
async function createFingerprintSourceAsync(source, limiter, projectRoot, options) {
let result = null;
switch (source.type) {
case 'contents':
result = await createContentsHashResultsAsync(source, options);
break;
case 'file':
result = await createFileHashResultsAsync(source.filePath, limiter, projectRoot, options);
break;
case 'dir':
result = await (0, Profile_1.profile)(options, createDirHashResultsAsync, `createDirHashResultsAsync(${source.filePath})`)(source.filePath, limiter, projectRoot, options);
break;
default:
throw new Error('Unsupported source type');
}
return {
...source,
hash: result?.hex ?? null,
...(options.debug ? { debugInfo: result?.debugInfo } : undefined),
};
}
/**
* Create a `HashResult` from a file
*/
async function createFileHashResultsAsync(filePath, limiter, projectRoot, options) {
// Backup code for faster hashing
/*
return limiter(async () => {
if (isIgnoredPathWithMatchObjects(filePath, options.ignorePathMatchObjects)) {
return null;
}
const hasher = createHash(options.hashAlgorithm);
const stat = await fs.stat(filePath);
hasher.update(`${stat.size}`);
const buffer = Buffer.alloc(4096);
const fd = await fs.open(filePath, 'r');
await fd.read(buffer, 0, buffer.length, 0);
await fd.close();
hasher.update(buffer);
console.log('stat', filePath, stat.size);
return { id: path.relative(projectRoot, filePath), hex: hasher.digest('hex') };
});
*/
return limiter(() => {
return new Promise((resolve, reject) => {
if ((0, Path_1.isIgnoredPathWithMatchObjects)(filePath, options.ignorePathMatchObjects)) {
return resolve(null);
}
let resolved = false;
const hasher = (0, crypto_1.createHash)(options.hashAlgorithm);
const fileHookTransform = options.fileHookTransform
? new FileHookTransform_1.FileHookTransform({ type: 'file', filePath }, options.fileHookTransform, options.debug)
: null;
let stream = (0, fs_1.createReadStream)(path_1.default.join(projectRoot, filePath), {
highWaterMark: 1024,
});
if (options.enableReactImportsPatcher &&
(filePath.endsWith('.h') || filePath.endsWith('.m') || filePath.endsWith('.mm'))) {
const transform = new ReactImportsPatcher_1.ReactImportsPatchTransform();
stream = (0, stream_1.pipeline)(stream, transform, (err) => {
if (err) {
reject(err);
}
});
}
if (fileHookTransform) {
stream = (0, stream_1.pipeline)(stream, fileHookTransform, (err) => {
if (err) {
reject(err);
}
});
}
stream.on('close', () => {
if (!resolved) {
const hex = hasher.digest('hex');
const isTransformed = fileHookTransform?.isTransformed;
const debugInfo = options.debug
? {
path: filePath,
hash: hex,
...(isTransformed ? { isTransformed } : undefined),
}
: undefined;
resolve({
type: 'file',
id: filePath,
hex,
...(debugInfo ? { debugInfo } : undefined),
});
resolved = true;
}
});
stream.on('error', (e) => {
reject(e);
});
stream.on('data', (chunk) => {
hasher.update(chunk);
});
});
});
}
/**
* Create `HashResult` for a dir.
* If the dir is excluded, returns null rather than a HashResult
*/
async function createDirHashResultsAsync(dirPath, limiter, projectRoot, options, depth = 0) {
// Using `ignoreDirMatchObjects` as an optimization to skip the whole directory
if ((0, Path_1.isIgnoredPathWithMatchObjects)(dirPath, options.ignoreDirMatchObjects)) {
return null;
}
const dirents = (await promises_1.default.readdir(path_1.default.join(projectRoot, dirPath), { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
const results = (await Promise.all(dirents.map(async (dirent) => {
if (dirent.isDirectory()) {
const filePath = (0, Path_1.toPosixPath)(path_1.default.join(dirPath, dirent.name));
return await createDirHashResultsAsync(filePath, limiter, projectRoot, options, depth + 1);
}
else if (dirent.isFile()) {
const filePath = (0, Path_1.toPosixPath)(path_1.default.join(dirPath, dirent.name));
return await createFileHashResultsAsync(filePath, limiter, projectRoot, options);
}
return null;
}))).filter(Predicates_1.nonNullish);
if (results.length === 0) {
return null;
}
const hasher = (0, crypto_1.createHash)(options.hashAlgorithm);
const children = [];
for (const result of results) {
hasher.update(result.id);
hasher.update(result.hex);
children.push(result.debugInfo);
}
const hex = hasher.digest('hex');
return {
type: 'dir',
id: dirPath,
hex,
...(options.debug ? { debugInfo: { path: dirPath, children, hash: hex } } : undefined),
};
}
/**
* Create `HashResult` for a `HashSourceContents`
*/
async function createContentsHashResultsAsync(source, options) {
let isTransformed = undefined;
if (options.fileHookTransform) {
const transformedContents = options.fileHookTransform({
type: 'contents',
id: source.id,
}, source.contents, true /* isEndOfFile */, 'utf8') ?? '';
if (options.debug) {
isTransformed = transformedContents !== source.contents;
}
source.contents = transformedContents;
}
const hex = (0, crypto_1.createHash)(options.hashAlgorithm).update(source.contents).digest('hex');
const debugInfo = options.debug
? {
hash: hex,
...(isTransformed ? { isTransformed } : undefined),
}
: undefined;
return {
type: 'contents',
id: source.id,
hex,
...(debugInfo ? { debugInfo } : undefined),
};
}
/**
* Create id from given source
*/
function createSourceId(source) {
switch (source.type) {
case 'contents':
return source.id;
case 'file':
return source.overrideHashKey ?? source.filePath;
case 'dir':
return source.overrideHashKey ?? source.filePath;
default:
throw new Error('Unsupported source type');
}
}
//# sourceMappingURL=Hash.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,31 @@
import { Transform, type TransformCallback } from 'stream';
interface Options {
/**
* Length of the file portion containing headers to transform.
* @default ReactImportsPatchTransform.DEFAULT_LENGTH_OF_FILE_PORTION_CONTAINING_HEADERS_TO_TRANSFORM
*/
lengthOfFilePortionContainingHeadersToTransform?: number;
/**
* A function that transforms a chunk of data. Exposing this for testing purposes.
* @default `patchChunk`
*/
transformFn?: (chunk: string) => string;
}
/**
* A transform stream that patches React import statements in Objective-C files.
*/
export declare class ReactImportsPatchTransform extends Transform {
private readLength;
private readonly lengthOfFilePortionContainingHeadersToTransform;
private readonly transformFn;
private static DEFAULT_LENGTH_OF_FILE_PORTION_CONTAINING_HEADERS_TO_TRANSFORM;
constructor(options?: Options);
_transform(chunk: any, _encoding: BufferEncoding, callback: TransformCallback): void;
}
/**
* Patch imports from a data chunk
* @param headerSet prebuilt React-Core header set
* @param chunk target chunk data
*/
export declare function patchChunk(chunk: string, headerSet?: Set<string>): string;
export {};
@@ -0,0 +1,347 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReactImportsPatchTransform = void 0;
exports.patchChunk = patchChunk;
const stream_1 = require("stream");
/**
* A transform stream that patches React import statements in Objective-C files.
*/
class ReactImportsPatchTransform extends stream_1.Transform {
readLength = 0;
lengthOfFilePortionContainingHeadersToTransform;
transformFn;
static DEFAULT_LENGTH_OF_FILE_PORTION_CONTAINING_HEADERS_TO_TRANSFORM = 16 * 1024; // 16KB
constructor(options) {
super();
this.lengthOfFilePortionContainingHeadersToTransform =
options?.lengthOfFilePortionContainingHeadersToTransform ??
ReactImportsPatchTransform.DEFAULT_LENGTH_OF_FILE_PORTION_CONTAINING_HEADERS_TO_TRANSFORM;
this.transformFn = options?.transformFn ?? patchChunk;
}
_transform(chunk, _encoding, callback) {
const remainingLength = this.lengthOfFilePortionContainingHeadersToTransform - this.readLength;
let result;
if (remainingLength <= 0) {
result = chunk.toString();
}
else if (remainingLength >= chunk.length) {
result = this.transformFn(chunk.toString());
}
else {
const portionToTransform = chunk.slice(0, remainingLength).toString();
const portionToLeave = chunk.slice(remainingLength).toString();
result = this.transformFn(portionToTransform) + portionToLeave;
}
this.push(result);
this.readLength += chunk.length;
callback();
}
}
exports.ReactImportsPatchTransform = ReactImportsPatchTransform;
/**
* Patch imports from a data chunk
* @param headerSet prebuilt React-Core header set
* @param chunk target chunk data
*/
function patchChunk(chunk, headerSet = HEADER_SET) {
let transformContent = chunk.replace(/(?<=^\s*)#import\s+"(.+)"(?=\s*$)/gm, (match, headerName) => {
// `#import "RCTBridge.h"` -> `#import <React/RCTBridge.h>`
if (headerSet.has(headerName)) {
return `#import <React/${headerName}>`;
}
// `#import "React/RCTBridge.h"` -> `#import <React/RCTBridge.h>`
if (headerName.startsWith('React/')) {
const name = headerName.substring(6);
if (headerSet.has(name)) {
return `#import <React/${name}>`;
}
}
// Otherwise, return original import
return match;
});
transformContent = transformContent.replace(/(?<=^\s*)#(if|elif)\s+__has_include\("(.+)"\)(?=\s*$)/gm, (match, ifPrefix, headerName) => {
// `#if __has_include("RCTBridge.h")` -> `#if __has_include(<React/RCTBridge.h>)`
if (headerSet.has(headerName)) {
return `#${ifPrefix} __has_include(<React/${headerName}>)`;
}
// `#if __has_include("React/RCTBridge.h")` -> `#if __has_include(<React/RCTBridge.h>)`
if (headerName.startsWith('React/')) {
const name = headerName.substring(6);
if (headerSet.has(name)) {
return `#${ifPrefix} __has_include(<React/${name}>)`;
}
}
// Otherwise, return original import
return match;
});
return transformContent;
}
// Fingerprint doesn't run `pod install` so we have a snapshot of the headers in the React-Core module from react-native 0.74.
const HEADER_SET = new Set([
'CoreModulesPlugins.h',
'FBXXHashUtils.h',
'NSTextStorage+FontScaling.h',
'RCTAccessibilityManager+Internal.h',
'RCTAccessibilityManager.h',
'RCTActionSheetManager.h',
'RCTActivityIndicatorView.h',
'RCTActivityIndicatorViewManager.h',
'RCTAdditionAnimatedNode.h',
'RCTAlertController.h',
'RCTAlertManager.h',
'RCTAnimatedImage.h',
'RCTAnimatedNode.h',
'RCTAnimationDriver.h',
'RCTAnimationPlugins.h',
'RCTAnimationType.h',
'RCTAnimationUtils.h',
'RCTAppState.h',
'RCTAppearance.h',
'RCTAssert.h',
'RCTAutoInsetsProtocol.h',
'RCTBackedTextInputDelegate.h',
'RCTBackedTextInputDelegateAdapter.h',
'RCTBackedTextInputViewProtocol.h',
'RCTBaseTextInputShadowView.h',
'RCTBaseTextInputView.h',
'RCTBaseTextInputViewManager.h',
'RCTBaseTextShadowView.h',
'RCTBaseTextViewManager.h',
'RCTBlobManager.h',
'RCTBorderCurve.h',
'RCTBorderDrawing.h',
'RCTBorderStyle.h',
'RCTBridge+Inspector.h',
'RCTBridge+Private.h',
'RCTBridge.h',
'RCTBridgeConstants.h',
'RCTBridgeDelegate.h',
'RCTBridgeMethod.h',
'RCTBridgeModule.h',
'RCTBridgeModuleDecorator.h',
'RCTBridgeProxy+Cxx.h',
'RCTBridgeProxy.h',
'RCTBundleAssetImageLoader.h',
'RCTBundleManager.h',
'RCTBundleURLProvider.h',
'RCTClipboard.h',
'RCTColorAnimatedNode.h',
'RCTComponent.h',
'RCTComponentData.h',
'RCTComponentEvent.h',
'RCTConstants.h',
'RCTConvert+CoreLocation.h',
'RCTConvert+Text.h',
'RCTConvert+Transform.h',
'RCTConvert.h',
'RCTCursor.h',
'RCTCxxConvert.h',
'RCTDataRequestHandler.h',
'RCTDebuggingOverlay.h',
'RCTDebuggingOverlayManager.h',
'RCTDecayAnimation.h',
'RCTDefines.h',
'RCTDevLoadingView.h',
'RCTDevLoadingViewProtocol.h',
'RCTDevLoadingViewSetEnabled.h',
'RCTDevMenu.h',
'RCTDevSettings.h',
'RCTDeviceInfo.h',
'RCTDiffClampAnimatedNode.h',
'RCTDisplayLink.h',
'RCTDisplayWeakRefreshable.h',
'RCTDivisionAnimatedNode.h',
'RCTDynamicTypeRamp.h',
'RCTErrorCustomizer.h',
'RCTErrorInfo.h',
'RCTEventAnimation.h',
'RCTEventDispatcher.h',
'RCTEventDispatcherProtocol.h',
'RCTEventEmitter.h',
'RCTExceptionsManager.h',
'RCTFPSGraph.h',
'RCTFileReaderModule.h',
'RCTFileRequestHandler.h',
'RCTFont.h',
'RCTFrameAnimation.h',
'RCTFrameUpdate.h',
'RCTGIFImageDecoder.h',
'RCTHTTPRequestHandler.h',
'RCTI18nManager.h',
'RCTI18nUtil.h',
'RCTImageBlurUtils.h',
'RCTImageCache.h',
'RCTImageDataDecoder.h',
'RCTImageEditingManager.h',
'RCTImageLoader.h',
'RCTImageLoaderLoggable.h',
'RCTImageLoaderProtocol.h',
'RCTImageLoaderWithAttributionProtocol.h',
'RCTImagePlugins.h',
'RCTImageShadowView.h',
'RCTImageSource.h',
'RCTImageStoreManager.h',
'RCTImageURLLoader.h',
'RCTImageURLLoaderWithAttribution.h',
'RCTImageUtils.h',
'RCTImageView.h',
'RCTImageViewManager.h',
'RCTInitializing.h',
'RCTInputAccessoryShadowView.h',
'RCTInputAccessoryView.h',
'RCTInputAccessoryViewContent.h',
'RCTInputAccessoryViewManager.h',
'RCTInspector.h',
'RCTInspectorDevServerHelper.h',
'RCTInspectorPackagerConnection.h',
'RCTInterpolationAnimatedNode.h',
'RCTInvalidating.h',
'RCTJSStackFrame.h',
'RCTJSThread.h',
'RCTJavaScriptExecutor.h',
'RCTJavaScriptLoader.h',
'RCTKeyCommands.h',
'RCTKeyboardObserver.h',
'RCTLayout.h',
'RCTLayoutAnimation.h',
'RCTLayoutAnimationGroup.h',
'RCTLinkingManager.h',
'RCTLinkingPlugins.h',
'RCTLocalAssetImageLoader.h',
'RCTLocalizedString.h',
'RCTLog.h',
'RCTLogBox.h',
'RCTLogBoxView.h',
'RCTMacros.h',
'RCTManagedPointer.h',
'RCTMockDef.h',
'RCTModalHostView.h',
'RCTModalHostViewController.h',
'RCTModalHostViewManager.h',
'RCTModalManager.h',
'RCTModuleData.h',
'RCTModuleMethod.h',
'RCTModuloAnimatedNode.h',
'RCTMultilineTextInputView.h',
'RCTMultilineTextInputViewManager.h',
'RCTMultipartDataTask.h',
'RCTMultipartStreamReader.h',
'RCTMultiplicationAnimatedNode.h',
'RCTNativeAnimatedModule.h',
'RCTNativeAnimatedNodesManager.h',
'RCTNativeAnimatedTurboModule.h',
'RCTNetworkPlugins.h',
'RCTNetworkTask.h',
'RCTNetworking.h',
'RCTNullability.h',
'RCTObjectAnimatedNode.h',
'RCTPLTag.h',
'RCTPackagerClient.h',
'RCTPackagerConnection.h',
'RCTParserUtils.h',
'RCTPerformanceLogger.h',
'RCTPerformanceLoggerLabels.h',
'RCTPlatform.h',
'RCTPointerEvents.h',
'RCTProfile.h',
'RCTPropsAnimatedNode.h',
'RCTRawTextShadowView.h',
'RCTRawTextViewManager.h',
'RCTReconnectingWebSocket.h',
'RCTRedBox.h',
'RCTRedBoxExtraDataViewController.h',
'RCTRedBoxSetEnabled.h',
'RCTRefreshControl.h',
'RCTRefreshControlManager.h',
'RCTRefreshableProtocol.h',
'RCTReloadCommand.h',
'RCTResizeMode.h',
'RCTRootContentView.h',
'RCTRootShadowView.h',
'RCTRootView.h',
'RCTRootViewDelegate.h',
'RCTRootViewInternal.h',
'RCTRuntimeExecutorModule.h',
'RCTSafeAreaShadowView.h',
'RCTSafeAreaView.h',
'RCTSafeAreaViewLocalData.h',
'RCTSafeAreaViewManager.h',
'RCTScrollContentShadowView.h',
'RCTScrollContentView.h',
'RCTScrollContentViewManager.h',
'RCTScrollEvent.h',
'RCTScrollView.h',
'RCTScrollViewManager.h',
'RCTScrollableProtocol.h',
'RCTSegmentedControl.h',
'RCTSegmentedControlManager.h',
'RCTSettingsManager.h',
'RCTSettingsPlugins.h',
'RCTShadowView+Internal.h',
'RCTShadowView+Layout.h',
'RCTShadowView.h',
'RCTSinglelineTextInputView.h',
'RCTSinglelineTextInputViewManager.h',
'RCTSourceCode.h',
'RCTSpringAnimation.h',
'RCTStatusBarManager.h',
'RCTStyleAnimatedNode.h',
'RCTSubtractionAnimatedNode.h',
'RCTSurface.h',
'RCTSurfaceDelegate.h',
'RCTSurfaceHostingProxyRootView.h',
'RCTSurfaceHostingView.h',
'RCTSurfacePresenterStub.h',
'RCTSurfaceProtocol.h',
'RCTSurfaceRootShadowView.h',
'RCTSurfaceRootShadowViewDelegate.h',
'RCTSurfaceRootView.h',
'RCTSurfaceSizeMeasureMode.h',
'RCTSurfaceStage.h',
'RCTSurfaceView+Internal.h',
'RCTSurfaceView.h',
'RCTSwitch.h',
'RCTSwitchManager.h',
'RCTTextAttributes.h',
'RCTTextDecorationLineType.h',
'RCTTextSelection.h',
'RCTTextShadowView.h',
'RCTTextTransform.h',
'RCTTextView.h',
'RCTTextViewManager.h',
'RCTTiming.h',
'RCTTouchEvent.h',
'RCTTouchHandler.h',
'RCTTrackingAnimatedNode.h',
'RCTTransformAnimatedNode.h',
'RCTTurboModuleRegistry.h',
'RCTUIImageViewAnimated.h',
'RCTUIManager.h',
'RCTUIManagerObserverCoordinator.h',
'RCTUIManagerUtils.h',
'RCTUITextField.h',
'RCTUITextView.h',
'RCTUIUtils.h',
'RCTURLRequestDelegate.h',
'RCTURLRequestHandler.h',
'RCTUtils.h',
'RCTUtilsUIOverride.h',
'RCTValueAnimatedNode.h',
'RCTVersion.h',
'RCTVibration.h',
'RCTVibrationPlugins.h',
'RCTView.h',
'RCTViewManager.h',
'RCTViewUtils.h',
'RCTVirtualTextShadowView.h',
'RCTVirtualTextView.h',
'RCTVirtualTextViewManager.h',
'RCTWebSocketExecutor.h',
'RCTWebSocketModule.h',
'RCTWrapperViewController.h',
'React-Core-umbrella.h',
'React-Core.modulemap',
'UIView+Private.h',
'UIView+React.h',
]);
//# sourceMappingURL=ReactImportsPatcher.js.map
File diff suppressed because one or more lines are too long
+4
View File
@@ -0,0 +1,4 @@
export * from './Fingerprint';
export * from './Fingerprint.types';
export * from './sourcer/SourceSkips';
export { DEFAULT_IGNORE_PATHS, DEFAULT_SOURCE_SKIPS } from './Options';
+24
View File
@@ -0,0 +1,24 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_SOURCE_SKIPS = exports.DEFAULT_IGNORE_PATHS = void 0;
__exportStar(require("./Fingerprint"), exports);
__exportStar(require("./Fingerprint.types"), exports);
__exportStar(require("./sourcer/SourceSkips"), exports);
var Options_1 = require("./Options");
Object.defineProperty(exports, "DEFAULT_IGNORE_PATHS", { enumerable: true, get: function () { return Options_1.DEFAULT_IGNORE_PATHS; } });
Object.defineProperty(exports, "DEFAULT_SOURCE_SKIPS", { enumerable: true, get: function () { return Options_1.DEFAULT_SOURCE_SKIPS; } });
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,gDAA8B;AAC9B,sDAAoC;AACpC,wDAAsC;AACtC,qCAAuE;AAA9D,+GAAA,oBAAoB,OAAA;AAAE,+GAAA,oBAAoB,OAAA"}
@@ -0,0 +1,8 @@
import type { HashSource, NormalizedOptions } from '../Fingerprint.types';
export declare function getBareAndroidSourcesAsync(projectRoot: string, options: NormalizedOptions): Promise<HashSource[]>;
export declare function getBareIosSourcesAsync(projectRoot: string, options: NormalizedOptions): Promise<HashSource[]>;
export declare function getPackageJsonScriptSourcesAsync(projectRoot: string, options: NormalizedOptions): Promise<HashSource[]>;
export declare function getGitIgnoreSourcesAsync(projectRoot: string, options: NormalizedOptions): Promise<HashSource[]>;
export declare function getCoreAutolinkingSourcesFromRncCliAsync(projectRoot: string, options: NormalizedOptions, useRNCoreAutolinkingFromExpo?: boolean): Promise<HashSource[]>;
export declare function getCoreAutolinkingSourcesFromExpoAndroid(projectRoot: string, options: NormalizedOptions, useRNCoreAutolinkingFromExpo?: boolean): Promise<HashSource[]>;
export declare function getCoreAutolinkingSourcesFromExpoIos(projectRoot: string, options: NormalizedOptions, useRNCoreAutolinkingFromExpo?: boolean): Promise<HashSource[]>;
+216
View File
@@ -0,0 +1,216 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getBareAndroidSourcesAsync = getBareAndroidSourcesAsync;
exports.getBareIosSourcesAsync = getBareIosSourcesAsync;
exports.getPackageJsonScriptSourcesAsync = getPackageJsonScriptSourcesAsync;
exports.getGitIgnoreSourcesAsync = getGitIgnoreSourcesAsync;
exports.getCoreAutolinkingSourcesFromRncCliAsync = getCoreAutolinkingSourcesFromRncCliAsync;
exports.getCoreAutolinkingSourcesFromExpoAndroid = getCoreAutolinkingSourcesFromExpoAndroid;
exports.getCoreAutolinkingSourcesFromExpoIos = getCoreAutolinkingSourcesFromExpoIos;
const spawn_async_1 = __importDefault(require("@expo/spawn-async"));
const assert_1 = __importDefault(require("assert"));
const chalk_1 = __importDefault(require("chalk"));
const node_process_1 = __importDefault(require("node:process"));
const path_1 = __importDefault(require("path"));
const resolve_from_1 = __importDefault(require("resolve-from"));
const ExpoResolver_1 = require("../ExpoResolver");
const SourceSkips_1 = require("./SourceSkips");
const Utils_1 = require("./Utils");
const Path_1 = require("../utils/Path");
const debug = require('debug')('expo:fingerprint:sourcer:Bare');
async function getBareAndroidSourcesAsync(projectRoot, options) {
if (options.platforms.includes('android')) {
const result = await (0, Utils_1.getFileBasedHashSourceAsync)(projectRoot, 'android', 'bareNativeDir');
if (result != null) {
debug(`Adding bare native dir - ${chalk_1.default.dim('android')}`);
return [result];
}
}
return [];
}
async function getBareIosSourcesAsync(projectRoot, options) {
if (options.platforms.includes('ios')) {
const result = await (0, Utils_1.getFileBasedHashSourceAsync)(projectRoot, 'ios', 'bareNativeDir');
if (result != null) {
debug(`Adding bare native dir - ${chalk_1.default.dim('ios')}`);
return [result];
}
}
return [];
}
async function getPackageJsonScriptSourcesAsync(projectRoot, options) {
if (options.sourceSkips & SourceSkips_1.SourceSkips.PackageJsonScriptsAll) {
return [];
}
let packageJson;
try {
packageJson = require((0, resolve_from_1.default)(path_1.default.resolve(projectRoot), './package.json'));
}
catch (e) {
debug(`Unable to read package.json from ${path_1.default.resolve(projectRoot)}/package.json: ` + e);
return [];
}
const results = [];
if (packageJson.scripts) {
debug(`Adding package.json contents - ${chalk_1.default.dim('scripts')}`);
const id = 'packageJson:scripts';
results.push({
type: 'contents',
id,
contents: normalizePackageJsonScriptSources(packageJson.scripts, options),
reasons: [id],
});
}
return results;
}
async function getGitIgnoreSourcesAsync(projectRoot, options) {
if (options.sourceSkips & SourceSkips_1.SourceSkips.GitIgnore) {
return [];
}
const result = await (0, Utils_1.getFileBasedHashSourceAsync)(projectRoot, '.gitignore', 'bareGitIgnore');
if (result != null) {
debug(`Adding file - ${chalk_1.default.dim('.gitignore')}`);
return [result];
}
return [];
}
async function getCoreAutolinkingSourcesFromRncCliAsync(projectRoot, options, useRNCoreAutolinkingFromExpo) {
if (useRNCoreAutolinkingFromExpo === true) {
return [];
}
try {
const { stdout } = await (0, spawn_async_1.default)('npx', ['react-native', 'config'], { cwd: projectRoot });
const config = JSON.parse(stdout);
const results = await parseCoreAutolinkingSourcesAsync({
config,
contentsId: 'rncoreAutolinkingConfig',
reasons: ['rncoreAutolinking'],
});
return results;
}
catch (e) {
debug(chalk_1.default.red(`Error adding react-native core autolinking sources.\n${e}`));
return [];
}
}
async function getCoreAutolinkingSourcesFromExpoAndroid(projectRoot, options, useRNCoreAutolinkingFromExpo) {
if (useRNCoreAutolinkingFromExpo === false || !options.platforms.includes('android')) {
return [];
}
const args = [
(0, ExpoResolver_1.resolveExpoAutolinkingCliPath)(projectRoot),
'react-native-config',
'--json',
'--platform',
'android',
];
try {
const { stdout } = await (0, spawn_async_1.default)('node', args, { cwd: projectRoot });
const config = JSON.parse(stdout);
const results = await parseCoreAutolinkingSourcesAsync({
config,
contentsId: 'rncoreAutolinkingConfig:android',
reasons: ['rncoreAutolinkingAndroid'],
platform: 'android',
});
return results;
}
catch (e) {
debug(chalk_1.default.red(`Error adding react-native core autolinking sources for android.\n${e}`));
return [];
}
}
async function getCoreAutolinkingSourcesFromExpoIos(projectRoot, options, useRNCoreAutolinkingFromExpo) {
if (useRNCoreAutolinkingFromExpo === false || !options.platforms.includes('ios')) {
return [];
}
try {
const { stdout } = await (0, spawn_async_1.default)('node', [
(0, ExpoResolver_1.resolveExpoAutolinkingCliPath)(projectRoot),
'react-native-config',
'--json',
'--platform',
'ios',
], { cwd: projectRoot });
const config = JSON.parse(stdout);
const results = await parseCoreAutolinkingSourcesAsync({
config,
contentsId: 'rncoreAutolinkingConfig:ios',
reasons: ['rncoreAutolinkingIos'],
platform: 'ios',
});
return results;
}
catch (e) {
debug(chalk_1.default.red(`Error adding react-native core autolinking sources for ios.\n${e}`));
return [];
}
}
async function parseCoreAutolinkingSourcesAsync({ config, reasons, contentsId, platform, }) {
const logTag = platform
? `react-native core autolinking dir for ${platform}`
: 'react-native core autolinking dir';
const results = [];
const { root } = config;
const autolinkingConfig = {};
for (const [depName, depData] of Object.entries(config.dependencies)) {
try {
stripRncoreAutolinkingAbsolutePaths(depData, root);
const filePath = (0, Path_1.toPosixPath)(depData.root);
debug(`Adding ${logTag} - ${chalk_1.default.dim(filePath)}`);
results.push({ type: 'dir', filePath, reasons });
autolinkingConfig[depName] = depData;
}
catch (e) {
debug(chalk_1.default.red(`Error adding ${logTag} - ${depName}.\n${e}`));
}
}
results.push({
type: 'contents',
id: contentsId,
contents: JSON.stringify(autolinkingConfig),
reasons,
});
return results;
}
function stripRncoreAutolinkingAbsolutePaths(dependency, root) {
(0, assert_1.default)(dependency.root);
const dependencyRoot = dependency.root;
const cmakeDepRoot = node_process_1.default.platform === 'win32' ? dependencyRoot.replace(/\\/g, '/') : dependencyRoot;
dependency.root = (0, Path_1.toPosixPath)(path_1.default.relative(root, dependencyRoot));
for (const platformData of Object.values(dependency.platforms)) {
for (const [key, value] of Object.entries(platformData ?? {})) {
let newValue;
if (node_process_1.default.platform === 'win32' &&
['cmakeListsPath', 'cxxModuleCMakeListsPath'].includes(key)) {
// CMake paths on Windows are serving in slashes,
// we have to check startsWith with the same slashes.
newValue = value?.startsWith?.(cmakeDepRoot)
? (0, Path_1.toPosixPath)(path_1.default.relative(root, value))
: value;
}
else {
newValue = value?.startsWith?.(dependencyRoot)
? (0, Path_1.toPosixPath)(path_1.default.relative(root, value))
: value;
}
platformData[key] = newValue;
}
}
}
function normalizePackageJsonScriptSources(scripts, options) {
if (options.sourceSkips & SourceSkips_1.SourceSkips.PackageJsonAndroidAndIosScriptsIfNotContainRun) {
// Replicate the behavior of `expo prebuild`
if (!scripts.android?.includes('run') || scripts.android === 'expo run:android') {
delete scripts.android;
}
if (!scripts.ios?.includes('run') || scripts.ios === 'expo run:ios') {
delete scripts.ios;
}
}
return JSON.stringify(scripts);
}
//# sourceMappingURL=Bare.js.map
File diff suppressed because one or more lines are too long
+23
View File
@@ -0,0 +1,23 @@
import type { ExpoConfig, ProjectConfig } from 'expo/config';
import type { HashSource, NormalizedOptions } from '../Fingerprint.types';
export declare function getExpoConfigSourcesAsync(projectRoot: string, config: ProjectConfig | null, loadedModules: string[] | null, options: NormalizedOptions): Promise<HashSource[]>;
export declare function createHashSourceExternalFileAsync({ projectRoot, file, reason, }: {
projectRoot: string;
file: string;
reason: string;
}): Promise<HashSource | null>;
export declare function getEasBuildSourcesAsync(projectRoot: string, options: NormalizedOptions): Promise<HashSource[]>;
export declare function getExpoAutolinkingAndroidSourcesAsync(projectRoot: string, options: NormalizedOptions, expoAutolinkingVersion: string): Promise<HashSource[]>;
/**
* Gets the patch sources for the `patch-project`.
*/
export declare function getExpoCNGPatchSourcesAsync(projectRoot: string, options: NormalizedOptions): Promise<HashSource[]>;
export declare function getExpoAutolinkingIosSourcesAsync(projectRoot: string, options: NormalizedOptions, expoAutolinkingVersion: string): Promise<HashSource[]>;
/**
* Sort the expo-modules-autolinking android config to make it stable from hashing.
*/
export declare function sortExpoAutolinkingAndroidConfig(config: Record<string, any>): Record<string, any>;
/**
* Get the props for a config-plugin
*/
export declare function getConfigPluginProps<Props>(config: ExpoConfig, pluginName: string): Props | null;
+340
View File
@@ -0,0 +1,340 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getExpoConfigSourcesAsync = getExpoConfigSourcesAsync;
exports.createHashSourceExternalFileAsync = createHashSourceExternalFileAsync;
exports.getEasBuildSourcesAsync = getEasBuildSourcesAsync;
exports.getExpoAutolinkingAndroidSourcesAsync = getExpoAutolinkingAndroidSourcesAsync;
exports.getExpoCNGPatchSourcesAsync = getExpoCNGPatchSourcesAsync;
exports.getExpoAutolinkingIosSourcesAsync = getExpoAutolinkingIosSourcesAsync;
exports.sortExpoAutolinkingAndroidConfig = sortExpoAutolinkingAndroidConfig;
exports.getConfigPluginProps = getConfigPluginProps;
const spawn_async_1 = __importDefault(require("@expo/spawn-async"));
const chalk_1 = __importDefault(require("chalk"));
const path_1 = __importDefault(require("path"));
const semver_1 = __importDefault(require("semver"));
const ExpoResolver_1 = require("../ExpoResolver");
const SourceSkips_1 = require("./SourceSkips");
const Utils_1 = require("./Utils");
const Path_1 = require("../utils/Path");
const debug = require('debug')('expo:fingerprint:sourcer:Expo');
async function getExpoConfigSourcesAsync(projectRoot, config, loadedModules, options) {
if (options.sourceSkips & SourceSkips_1.SourceSkips.ExpoConfigAll) {
return [];
}
if (config == null) {
return [];
}
const results = [];
let expoConfig = normalizeExpoConfig(config.exp, projectRoot, options);
// external files in config
const isAndroid = options.platforms.includes('android');
const isIos = options.platforms.includes('ios');
const splashScreenPluginProps = getConfigPluginProps(expoConfig, 'expo-splash-screen');
const externalFiles = [
// icons
expoConfig.icon,
isAndroid ? expoConfig.android?.icon : undefined,
...(isIos ? collectIosIcons(expoConfig.ios?.icon) : []),
isAndroid ? expoConfig.android?.adaptiveIcon?.foregroundImage : undefined,
isAndroid ? expoConfig.android?.adaptiveIcon?.backgroundImage : undefined,
expoConfig.notification?.icon,
// expo-splash-screen images
splashScreenPluginProps?.image,
splashScreenPluginProps?.dark?.image,
isAndroid ? splashScreenPluginProps?.android?.image : undefined,
isAndroid ? splashScreenPluginProps?.android?.mdpi : undefined,
isAndroid ? splashScreenPluginProps?.android?.hdpi : undefined,
isAndroid ? splashScreenPluginProps?.android?.xhdpi : undefined,
isAndroid ? splashScreenPluginProps?.android?.xxhdpi : undefined,
isAndroid ? splashScreenPluginProps?.android?.xxxhdpi : undefined,
isAndroid ? splashScreenPluginProps?.android?.dark?.image : undefined,
isAndroid ? splashScreenPluginProps?.android?.dark?.mdpi : undefined,
isAndroid ? splashScreenPluginProps?.android?.dark?.hdpi : undefined,
isAndroid ? splashScreenPluginProps?.android?.dark?.xhdpi : undefined,
isAndroid ? splashScreenPluginProps?.android?.dark?.xxhdpi : undefined,
isAndroid ? splashScreenPluginProps?.android?.dark?.xxxhdpi : undefined,
isIos ? splashScreenPluginProps?.ios?.image : undefined,
isIos ? splashScreenPluginProps?.ios?.tabletImage : undefined,
isIos ? splashScreenPluginProps?.ios?.dark?.image : undefined,
isIos ? splashScreenPluginProps?.ios?.dark?.tabletImage : undefined,
// legacy splash images
expoConfig.splash?.image,
isAndroid ? expoConfig.android?.splash?.image : undefined,
isAndroid ? expoConfig.android?.splash?.mdpi : undefined,
isAndroid ? expoConfig.android?.splash?.hdpi : undefined,
isAndroid ? expoConfig.android?.splash?.xhdpi : undefined,
isAndroid ? expoConfig.android?.splash?.xxhdpi : undefined,
isAndroid ? expoConfig.android?.splash?.xxxhdpi : undefined,
isIos ? expoConfig.ios?.splash?.image : undefined,
isIos ? expoConfig.ios?.splash?.tabletImage : undefined,
// google service files
isAndroid ? expoConfig.android?.googleServicesFile : undefined,
isIos ? expoConfig.ios?.googleServicesFile : undefined,
]
.filter((file) => Boolean(file))
.map((filePath) => ensureRelativePath(projectRoot, filePath));
const externalFileSources = (await Promise.all(externalFiles.map((file) => createHashSourceExternalFileAsync({ projectRoot, file, reason: 'expoConfigExternalFile' })))).filter(Boolean);
results.push(...externalFileSources);
expoConfig = postUpdateExpoConfig(expoConfig, projectRoot);
results.push({
type: 'contents',
id: 'expoConfig',
contents: (0, Utils_1.stringifyJsonSorted)(expoConfig),
reasons: ['expoConfig'],
});
// config plugins
const configPluginModules = (loadedModules ?? []).map((modulePath) => ({
type: 'file',
filePath: (0, Path_1.toPosixPath)(modulePath),
reasons: ['expoConfigPlugins'],
}));
results.push(...configPluginModules);
return results;
}
function normalizeExpoConfig(config, projectRoot, options) {
// Deep clone by JSON.parse/stringify that assumes the config is serializable.
const normalizedConfig = JSON.parse(JSON.stringify(config));
const { sourceSkips } = options;
delete normalizedConfig._internal;
if (sourceSkips & SourceSkips_1.SourceSkips.ExpoConfigVersions) {
delete normalizedConfig.version;
delete normalizedConfig.android?.versionCode;
delete normalizedConfig.ios?.buildNumber;
}
if (sourceSkips & SourceSkips_1.SourceSkips.ExpoConfigRuntimeVersionIfString) {
if (typeof normalizedConfig.runtimeVersion === 'string') {
delete normalizedConfig.runtimeVersion;
}
if (typeof normalizedConfig.android?.runtimeVersion === 'string') {
delete normalizedConfig.android.runtimeVersion;
}
if (typeof normalizedConfig.ios?.runtimeVersion === 'string') {
delete normalizedConfig.ios.runtimeVersion;
}
if (typeof normalizedConfig.web?.runtimeVersion === 'string') {
delete normalizedConfig.web.runtimeVersion;
}
}
if (sourceSkips & SourceSkips_1.SourceSkips.ExpoConfigNames) {
normalizedConfig.name = '';
delete normalizedConfig.description;
delete normalizedConfig.web?.name;
delete normalizedConfig.web?.shortName;
delete normalizedConfig.web?.description;
}
if (sourceSkips & SourceSkips_1.SourceSkips.ExpoConfigAndroidPackage) {
delete normalizedConfig.android?.package;
}
if (sourceSkips & SourceSkips_1.SourceSkips.ExpoConfigIosBundleIdentifier) {
delete normalizedConfig.ios?.bundleIdentifier;
}
if (sourceSkips & SourceSkips_1.SourceSkips.ExpoConfigSchemes) {
delete normalizedConfig.scheme;
normalizedConfig.slug = '';
}
if (sourceSkips & SourceSkips_1.SourceSkips.ExpoConfigEASProject) {
delete normalizedConfig.owner;
delete normalizedConfig?.extra?.eas;
delete normalizedConfig?.updates?.url;
}
if (sourceSkips & SourceSkips_1.SourceSkips.ExpoConfigAssets) {
delete normalizedConfig.icon;
delete normalizedConfig.splash;
delete normalizedConfig.android?.adaptiveIcon;
delete normalizedConfig.android?.icon;
delete normalizedConfig.android?.splash;
delete normalizedConfig.ios?.icon;
delete normalizedConfig.ios?.splash;
delete normalizedConfig.web?.favicon;
delete normalizedConfig.web?.splash;
}
if (sourceSkips & SourceSkips_1.SourceSkips.ExpoConfigExtraSection) {
delete normalizedConfig.extra;
}
return (0, Utils_1.relativizeJsonPaths)(normalizedConfig, projectRoot);
}
/**
* Gives the last chance to modify the ExpoConfig.
* For example, we can remove some fields that are already included in the fingerprint.
*/
function postUpdateExpoConfig(config, projectRoot) {
// The config is already a clone, so we can modify it in place for performance.
// googleServicesFile may contain absolute paths on EAS with file-based secrets.
// Given we include googleServicesFile as external files already, we can remove it from the config.
delete config.android?.googleServicesFile;
delete config.ios?.googleServicesFile;
return config;
}
/**
* Collect iOS icon to flattened file paths.
*/
function collectIosIcons(icon) {
if (icon == null) {
return [];
}
if (typeof icon === 'string') {
return [icon];
}
return [icon.light, icon.dark, icon.tinted].filter((file) => Boolean(file));
}
/**
* The filePath in config could be relative (`./assets/icon.png`, `assets/icon.png`) or even absolute.
* We need to normalize the path and return as relative path without `./` prefix.
*/
function ensureRelativePath(projectRoot, filePath) {
const absolutePath = path_1.default.resolve(projectRoot, filePath);
return path_1.default.relative(projectRoot, absolutePath);
}
async function createHashSourceExternalFileAsync({ projectRoot, file, reason, }) {
const hashSource = await (0, Utils_1.getFileBasedHashSourceAsync)(projectRoot, file, reason);
if (hashSource) {
debug(`Adding config external file - ${chalk_1.default.dim(file)}`);
if (hashSource.type === 'file' || hashSource.type === 'dir') {
// We include the expo config contents in the fingerprint,
// the `filePath` hashing for the external files is not necessary.
// Especially people using EAS environment variables for the google service files,
// the `filePath` will be different between local and remote builds.
// We use a fixed override hash key and basically ignore the `filePath` hashing.
hashSource.overrideHashKey = 'expoConfigExternalFile:contentsOnly';
}
}
return hashSource;
}
async function getEasBuildSourcesAsync(projectRoot, options) {
const files = ['eas.json', '.easignore'];
const results = (await Promise.all(files.map(async (file) => {
const result = await (0, Utils_1.getFileBasedHashSourceAsync)(projectRoot, file, 'easBuild');
if (result != null) {
debug(`Adding eas file - ${chalk_1.default.dim(file)}`);
}
return result;
}))).filter(Boolean);
return results;
}
async function getExpoAutolinkingAndroidSourcesAsync(projectRoot, options, expoAutolinkingVersion) {
if (!options.platforms.includes('android')) {
return [];
}
try {
const reasons = ['expoAutolinkingAndroid'];
const results = [];
const { stdout } = await (0, spawn_async_1.default)('node', [(0, ExpoResolver_1.resolveExpoAutolinkingCliPath)(projectRoot), 'resolve', '-p', 'android', '--json'], { cwd: projectRoot });
const config = sortExpoAutolinkingAndroidConfig(JSON.parse(stdout));
for (const module of config.modules) {
for (const project of module.projects) {
const filePath = (0, Path_1.toPosixPath)(path_1.default.relative(projectRoot, project.sourceDir));
project.sourceDir = filePath; // use relative path for the dir
debug(`Adding expo-modules-autolinking android dir - ${chalk_1.default.dim(filePath)}`);
results.push({ type: 'dir', filePath, reasons });
// `aarProjects` is present in project starting from SDK 53+.
if (project.aarProjects) {
for (const aarProject of project.aarProjects) {
// use relative path for aarProject fields
aarProject.aarFilePath = (0, Path_1.toPosixPath)(path_1.default.relative(projectRoot, aarProject.aarFilePath));
aarProject.projectDir = (0, Path_1.toPosixPath)(path_1.default.relative(projectRoot, aarProject.projectDir));
}
}
if (typeof project.shouldUsePublicationScriptPath === 'string') {
project.shouldUsePublicationScriptPath = (0, Path_1.toPosixPath)(path_1.default.relative(projectRoot, project.shouldUsePublicationScriptPath));
}
}
if (module.plugins) {
for (const plugin of module.plugins) {
const filePath = (0, Path_1.toPosixPath)(path_1.default.relative(projectRoot, plugin.sourceDir));
plugin.sourceDir = filePath; // use relative path for the dir
debug(`Adding expo-modules-autolinking android dir - ${chalk_1.default.dim(filePath)}`);
results.push({ type: 'dir', filePath, reasons });
}
}
// Backward compatibility for SDK versions earlier than 53
if (module.aarProjects) {
for (const aarProject of module.aarProjects) {
// use relative path for aarProject fields
aarProject.aarFilePath = (0, Path_1.toPosixPath)(path_1.default.relative(projectRoot, aarProject.aarFilePath));
aarProject.projectDir = (0, Path_1.toPosixPath)(path_1.default.relative(projectRoot, aarProject.projectDir));
}
}
}
results.push({
type: 'contents',
id: 'expoAutolinkingConfig:android',
contents: JSON.stringify(config),
reasons,
});
return results;
}
catch {
return [];
}
}
/**
* Gets the patch sources for the `patch-project`.
*/
async function getExpoCNGPatchSourcesAsync(projectRoot, options) {
const result = await (0, Utils_1.getFileBasedHashSourceAsync)(projectRoot, 'cng-patches', 'expoCNGPatches');
if (result != null) {
debug(`Adding dir - ${chalk_1.default.dim('cng-patches')}`);
return [result];
}
return [];
}
async function getExpoAutolinkingIosSourcesAsync(projectRoot, options, expoAutolinkingVersion) {
if (!options.platforms.includes('ios')) {
return [];
}
// expo-modules-autolinking 1.10.0 added support for apple platform
const platform = semver_1.default.lt(expoAutolinkingVersion, '1.10.0') ? 'ios' : 'apple';
try {
const reasons = ['expoAutolinkingIos'];
const results = [];
const { stdout } = await (0, spawn_async_1.default)('node', [(0, ExpoResolver_1.resolveExpoAutolinkingCliPath)(projectRoot), 'resolve', '-p', platform, '--json'], { cwd: projectRoot });
const config = JSON.parse(stdout);
for (const module of config.modules) {
for (const pod of module.pods) {
const filePath = (0, Path_1.toPosixPath)(path_1.default.relative(projectRoot, pod.podspecDir));
pod.podspecDir = filePath; // use relative path for the dir
debug(`Adding expo-modules-autolinking ios dir - ${chalk_1.default.dim(filePath)}`);
results.push({ type: 'dir', filePath, reasons });
}
}
results.push({
type: 'contents',
id: 'expoAutolinkingConfig:ios',
contents: JSON.stringify(config),
reasons,
});
return results;
}
catch {
return [];
}
}
/**
* Sort the expo-modules-autolinking android config to make it stable from hashing.
*/
function sortExpoAutolinkingAndroidConfig(config) {
for (const module of config.modules) {
// Sort the projects by project.name
module.projects.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
}
return config;
}
/**
* Get the props for a config-plugin
*/
function getConfigPluginProps(config, pluginName) {
const plugin = (config.plugins ?? []).find((plugin) => {
if (Array.isArray(plugin)) {
return plugin[0] === pluginName;
}
return plugin === pluginName;
});
if (Array.isArray(plugin)) {
return (plugin[1] ?? null);
}
return null;
}
//# sourceMappingURL=Expo.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,18 @@
import type { HashSource, NormalizedOptions } from '../Fingerprint.types';
interface PackageSourcerParams {
/**
* The package name.
*
* Note that the package should be a direct dependency or devDependency of the project.
* Otherwise on pnpm isolated mode the resolution will fail.
*/
packageName: string;
/**
* Hashing **package.json** file for the package rather than the entire directory.
* This is useful when the package contains a lot of files.
*/
packageJsonOnly: boolean;
}
export declare function getDefaultPackageSourcesAsync(projectRoot: string, options: NormalizedOptions): Promise<HashSource[]>;
export declare function getPackageSourceAsync(projectRoot: string, params: PackageSourcerParams): Promise<HashSource | null>;
export {};
@@ -0,0 +1,41 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDefaultPackageSourcesAsync = getDefaultPackageSourcesAsync;
exports.getPackageSourceAsync = getPackageSourceAsync;
const chalk_1 = __importDefault(require("chalk"));
const path_1 = __importDefault(require("path"));
const resolve_from_1 = __importDefault(require("resolve-from"));
const Utils_1 = require("./Utils");
const debug = require('debug')('expo:fingerprint:sourcer:Packages');
const DEFAULT_PACKAGES = [
{
packageName: 'react-native',
packageJsonOnly: true,
},
];
async function getDefaultPackageSourcesAsync(projectRoot, options) {
const results = await Promise.all(DEFAULT_PACKAGES.map((params) => getPackageSourceAsync(projectRoot, params)));
return results.filter(Boolean);
}
async function getPackageSourceAsync(projectRoot, params) {
const reason = `package:${params.packageName}`;
const packageJsonPath = resolve_from_1.default.silent(projectRoot, `${params.packageName}/package.json`);
if (packageJsonPath == null) {
return null;
}
debug(`Adding package - ${chalk_1.default.dim(params.packageName)}`);
if (params.packageJsonOnly) {
return {
type: 'contents',
id: reason,
contents: JSON.stringify(require(packageJsonPath)), // keep the json collapsed by serializing/deserializing
reasons: [reason],
};
}
const packageRoot = path_1.default.relative(projectRoot, path_1.default.dirname(packageJsonPath));
return await (0, Utils_1.getFileBasedHashSourceAsync)(projectRoot, packageRoot, reason);
}
//# sourceMappingURL=Packages.js.map
@@ -0,0 +1 @@
{"version":3,"file":"Packages.js","sourceRoot":"","sources":["../../src/sourcer/Packages.ts"],"names":[],"mappings":";;;;;AAgCA,sEAQC;AAED,sDAuBC;AAjED,kDAA0B;AAC1B,gDAAwB;AACxB,gEAAuC;AAEvC,mCAAsD;AAGtD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,mCAAmC,CAAC,CAAC;AAkBpE,MAAM,gBAAgB,GAA2B;IAC/C;QACE,WAAW,EAAE,cAAc;QAC3B,eAAe,EAAE,IAAI;KACtB;CACF,CAAC;AAEK,KAAK,UAAU,6BAA6B,CACjD,WAAmB,EACnB,OAA0B;IAE1B,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,gBAAgB,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,qBAAqB,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAC7E,CAAC;IACF,OAAO,OAAO,CAAC,MAAM,CAAC,OAAO,CAAiB,CAAC;AACjD,CAAC;AAEM,KAAK,UAAU,qBAAqB,CACzC,WAAmB,EACnB,MAA4B;IAE5B,MAAM,MAAM,GAAG,WAAW,MAAM,CAAC,WAAW,EAAE,CAAC;IAC/C,MAAM,eAAe,GAAG,sBAAW,CAAC,MAAM,CAAC,WAAW,EAAE,GAAG,MAAM,CAAC,WAAW,eAAe,CAAC,CAAC;IAC9F,IAAI,eAAe,IAAI,IAAI,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,oBAAoB,eAAK,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IAE3D,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;QAC3B,OAAO;YACL,IAAI,EAAE,UAAU;YAChB,EAAE,EAAE,MAAM;YACV,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,EAAE,uDAAuD;YAC3G,OAAO,EAAE,CAAC,MAAM,CAAC;SAClB,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAG,cAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,cAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC;IAC9E,OAAO,MAAM,IAAA,mCAA2B,EAAC,WAAW,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;AAC7E,CAAC"}
@@ -0,0 +1,2 @@
import type { HashSource, NormalizedOptions } from '../Fingerprint.types';
export declare function getPatchPackageSourcesAsync(projectRoot: string, options: NormalizedOptions): Promise<HashSource[]>;
@@ -0,0 +1,23 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPatchPackageSourcesAsync = getPatchPackageSourcesAsync;
const chalk_1 = __importDefault(require("chalk"));
const Utils_1 = require("./Utils");
const Path_1 = require("../utils/Path");
const debug = require('debug')('expo:fingerprint:sourcer:PatchPackage');
async function getPatchPackageSourcesAsync(projectRoot, options) {
if ((0, Path_1.isIgnoredPathWithMatchObjects)('patches', options.ignoreDirMatchObjects)) {
debug(`Skipping dir - ${chalk_1.default.dim('patches')} (ignored by ignoreDirMatchObjects)`);
return [];
}
const result = await (0, Utils_1.getFileBasedHashSourceAsync)(projectRoot, 'patches', 'patchPackage');
if (result != null) {
debug(`Adding dir - ${chalk_1.default.dim('patches')}`);
return [result];
}
return [];
}
//# sourceMappingURL=PatchPackage.js.map
@@ -0,0 +1 @@
{"version":3,"file":"PatchPackage.js","sourceRoot":"","sources":["../../src/sourcer/PatchPackage.ts"],"names":[],"mappings":";;;;;AAQA,kEAcC;AAtBD,kDAA0B;AAE1B,mCAAsD;AAEtD,wCAA8D;AAE9D,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,uCAAuC,CAAC,CAAC;AAEjE,KAAK,UAAU,2BAA2B,CAC/C,WAAmB,EACnB,OAA0B;IAE1B,IAAI,IAAA,oCAA6B,EAAC,SAAS,EAAE,OAAO,CAAC,qBAAqB,CAAC,EAAE,CAAC;QAC5E,KAAK,CAAC,kBAAkB,eAAK,CAAC,GAAG,CAAC,SAAS,CAAC,qCAAqC,CAAC,CAAC;QACnF,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,IAAA,mCAA2B,EAAC,WAAW,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC;IACzF,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;QACnB,KAAK,CAAC,gBAAgB,eAAK,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAC9C,OAAO,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC"}
@@ -0,0 +1,46 @@
/**
* Bitmask of values that can be used to skip certain parts of the sourcers when generating a fingerprint.
*/
export declare enum SourceSkips {
/** Skip nothing */
None = 0,
/** Versions in app.json, including Android versionCode and iOS buildNumber */
ExpoConfigVersions = 1,
/** runtimeVersion in app.json if it is a string */
ExpoConfigRuntimeVersionIfString = 2,
/** App names in app.json, including shortName and description */
ExpoConfigNames = 4,
/** Android package name in app.json */
ExpoConfigAndroidPackage = 8,
/** iOS bundle identifier in app.json */
ExpoConfigIosBundleIdentifier = 16,
/** Schemes in app.json */
ExpoConfigSchemes = 32,
/** EAS project information in app.json */
ExpoConfigEASProject = 64,
/** Assets in app.json, including icons and splash assets */
ExpoConfigAssets = 128,
/**
* Skip 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.
*/
ExpoConfigAll = 256,
/**
* package.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.
*/
PackageJsonAndroidAndIosScriptsIfNotContainRun = 512,
/**
* Skip the whole `scripts` section in the project's package.json.
*/
PackageJsonScriptsAll = 1024,
/**
* Skip .gitignore files.
*/
GitIgnore = 2048,
/** The [extra](https://docs.expo.dev/versions/latest/config/app/#extra) section in app.json */
ExpoConfigExtraSection = 4096
}
@@ -0,0 +1,53 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SourceSkips = void 0;
/**
* Bitmask of values that can be used to skip certain parts of the sourcers when generating a fingerprint.
*/
var SourceSkips;
(function (SourceSkips) {
/** Skip nothing */
SourceSkips[SourceSkips["None"] = 0] = "None";
//#region - ExpoConfig source (e.g., app.json, app.config.js, etc.)
/** Versions in app.json, including Android versionCode and iOS buildNumber */
SourceSkips[SourceSkips["ExpoConfigVersions"] = 1] = "ExpoConfigVersions";
/** runtimeVersion in app.json if it is a string */
SourceSkips[SourceSkips["ExpoConfigRuntimeVersionIfString"] = 2] = "ExpoConfigRuntimeVersionIfString";
/** App names in app.json, including shortName and description */
SourceSkips[SourceSkips["ExpoConfigNames"] = 4] = "ExpoConfigNames";
/** Android package name in app.json */
SourceSkips[SourceSkips["ExpoConfigAndroidPackage"] = 8] = "ExpoConfigAndroidPackage";
/** iOS bundle identifier in app.json */
SourceSkips[SourceSkips["ExpoConfigIosBundleIdentifier"] = 16] = "ExpoConfigIosBundleIdentifier";
/** Schemes in app.json */
SourceSkips[SourceSkips["ExpoConfigSchemes"] = 32] = "ExpoConfigSchemes";
/** EAS project information in app.json */
SourceSkips[SourceSkips["ExpoConfigEASProject"] = 64] = "ExpoConfigEASProject";
/** Assets in app.json, including icons and splash assets */
SourceSkips[SourceSkips["ExpoConfigAssets"] = 128] = "ExpoConfigAssets";
/**
* Skip 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[SourceSkips["ExpoConfigAll"] = 256] = "ExpoConfigAll";
//#endregion - ExpoConfig source (e.g., app.json, app.config.js, etc.)
/**
* package.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[SourceSkips["PackageJsonAndroidAndIosScriptsIfNotContainRun"] = 512] = "PackageJsonAndroidAndIosScriptsIfNotContainRun";
/**
* Skip the whole `scripts` section in the project's package.json.
*/
SourceSkips[SourceSkips["PackageJsonScriptsAll"] = 1024] = "PackageJsonScriptsAll";
/**
* Skip .gitignore files.
*/
SourceSkips[SourceSkips["GitIgnore"] = 2048] = "GitIgnore";
/** The [extra](https://docs.expo.dev/versions/latest/config/app/#extra) section in app.json */
SourceSkips[SourceSkips["ExpoConfigExtraSection"] = 4096] = "ExpoConfigExtraSection";
})(SourceSkips || (exports.SourceSkips = SourceSkips = {}));
//# sourceMappingURL=SourceSkips.js.map
@@ -0,0 +1 @@
{"version":3,"file":"SourceSkips.js","sourceRoot":"","sources":["../../src/sourcer/SourceSkips.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACH,IAAY,WA2DX;AA3DD,WAAY,WAAW;IACrB,mBAAmB;IACnB,6CAAQ,CAAA;IAER,mEAAmE;IAEnE,8EAA8E;IAC9E,yEAA2B,CAAA;IAE3B,mDAAmD;IACnD,qGAAyC,CAAA;IAEzC,iEAAiE;IACjE,mEAAwB,CAAA;IAExB,uCAAuC;IACvC,qFAAiC,CAAA;IAEjC,wCAAwC;IACxC,gGAAsC,CAAA;IAEtC,0BAA0B;IAC1B,wEAA0B,CAAA;IAE1B,0CAA0C;IAC1C,8EAA6B,CAAA;IAE7B,4DAA4D;IAC5D,uEAAyB,CAAA;IAEzB;;;;;OAKG;IACH,iEAAsB,CAAA;IAEtB,sEAAsE;IAEtE;;;;OAIG;IACH,mIAAuD,CAAA;IAEvD;;OAEG;IACH,kFAA+B,CAAA;IAE/B;;OAEG;IACH,0DAAmB,CAAA;IAEnB,+FAA+F;IAC/F,oFAAgC,CAAA;AAClC,CAAC,EA3DW,WAAW,2BAAX,WAAW,QA2DtB"}
@@ -0,0 +1,2 @@
import type { HashSource, NormalizedOptions } from '../Fingerprint.types';
export declare function getHashSourcesAsync(projectRoot: string, options: NormalizedOptions): Promise<HashSource[]>;
+59
View File
@@ -0,0 +1,59 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getHashSourcesAsync = getHashSourcesAsync;
const chalk_1 = __importDefault(require("chalk"));
const semver_1 = __importDefault(require("semver"));
const Bare_1 = require("./Bare");
const Expo_1 = require("./Expo");
const ExpoConfig_1 = require("../ExpoConfig");
const ExpoResolver_1 = require("../ExpoResolver");
const Packages_1 = require("./Packages");
const PatchPackage_1 = require("./PatchPackage");
const Profile_1 = require("../utils/Profile");
const debug = require('debug')('expo:fingerprint:sourcer:Sourcer');
async function getHashSourcesAsync(projectRoot, options) {
const { config: expoConfig, loadedModules } = await (0, ExpoConfig_1.getExpoConfigAsync)(projectRoot, options);
const expoAutolinkingVersion = (0, ExpoResolver_1.resolveExpoAutolinkingVersion)(projectRoot) ?? '0.0.0';
const useRNCoreAutolinkingFromExpo =
// expo-modules-autolinking supports the `react-native-config` core autolinking from 1.11.2.
// To makes the `useRNCoreAutolinkingFromExpo` default to `true` for Expo SDK 52 and higher.
// We check the expo-modules-autolinking version from 1.12.0.
typeof options.useRNCoreAutolinkingFromExpo === 'boolean'
? options.useRNCoreAutolinkingFromExpo
: semver_1.default.gte(expoAutolinkingVersion, '1.12.0');
const results = await Promise.all([
// expo
(0, Profile_1.profile)(options, Expo_1.getExpoAutolinkingAndroidSourcesAsync)(projectRoot, options, expoAutolinkingVersion),
(0, Profile_1.profile)(options, Expo_1.getExpoAutolinkingIosSourcesAsync)(projectRoot, options, expoAutolinkingVersion),
(0, Profile_1.profile)(options, Expo_1.getExpoConfigSourcesAsync)(projectRoot, expoConfig, loadedModules, options),
(0, Profile_1.profile)(options, Expo_1.getEasBuildSourcesAsync)(projectRoot, options),
(0, Profile_1.profile)(options, Expo_1.getExpoCNGPatchSourcesAsync)(projectRoot, options),
// bare managed files
(0, Profile_1.profile)(options, Bare_1.getGitIgnoreSourcesAsync)(projectRoot, options),
(0, Profile_1.profile)(options, Bare_1.getPackageJsonScriptSourcesAsync)(projectRoot, options),
// bare native files
(0, Profile_1.profile)(options, Bare_1.getBareAndroidSourcesAsync)(projectRoot, options),
(0, Profile_1.profile)(options, Bare_1.getBareIosSourcesAsync)(projectRoot, options),
// react-native core autolinking
(0, Profile_1.profile)(options, Bare_1.getCoreAutolinkingSourcesFromExpoAndroid)(projectRoot, options, useRNCoreAutolinkingFromExpo),
(0, Profile_1.profile)(options, Bare_1.getCoreAutolinkingSourcesFromExpoIos)(projectRoot, options, useRNCoreAutolinkingFromExpo),
(0, Profile_1.profile)(options, Bare_1.getCoreAutolinkingSourcesFromRncCliAsync)(projectRoot, options, useRNCoreAutolinkingFromExpo),
// patch-package
(0, Profile_1.profile)(options, PatchPackage_1.getPatchPackageSourcesAsync)(projectRoot, options),
// some known dependencies, e.g. react-native
(0, Profile_1.profile)(options, Packages_1.getDefaultPackageSourcesAsync)(projectRoot, options),
]);
// extra sources
if (options.extraSources) {
for (const source of options.extraSources) {
debug(`Adding extra source - ${chalk_1.default.dim(JSON.stringify(source))}`);
}
results.push(options.extraSources);
}
// flatten results
return [].concat(...results);
}
//# sourceMappingURL=Sourcer.js.map
@@ -0,0 +1 @@
{"version":3,"file":"Sourcer.js","sourceRoot":"","sources":["../../src/sourcer/Sourcer.ts"],"names":[],"mappings":"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"}
+10
View File
@@ -0,0 +1,10 @@
import type { HashSource } from '../Fingerprint.types';
export declare function getFileBasedHashSourceAsync(projectRoot: string, filePath: string, reason: string): Promise<HashSource | null>;
/**
* A version of `JSON.stringify` that keeps the keys sorted
*/
export declare function stringifyJsonSorted(target: any, space?: string | number | undefined): string;
/**
* Transform absolute paths in JSON to relative paths based on the project root.
*/
export declare function relativizeJsonPaths(value: any, projectRoot: string): any;
+78
View File
@@ -0,0 +1,78 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFileBasedHashSourceAsync = getFileBasedHashSourceAsync;
exports.stringifyJsonSorted = stringifyJsonSorted;
exports.relativizeJsonPaths = relativizeJsonPaths;
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
const Path_1 = require("../utils/Path");
async function getFileBasedHashSourceAsync(projectRoot, filePath, reason) {
let result = null;
try {
const stat = await promises_1.default.stat(path_1.default.join(projectRoot, filePath));
result = {
type: stat.isDirectory() ? 'dir' : 'file',
filePath: (0, Path_1.toPosixPath)(filePath),
reasons: [reason],
};
}
catch {
result = null;
}
return result;
}
/**
* A version of `JSON.stringify` that keeps the keys sorted
*/
function stringifyJsonSorted(target, space) {
return JSON.stringify(target, (_, value) => sortJson(value), space);
}
/**
* Transform absolute paths in JSON to relative paths based on the project root.
*/
function relativizeJsonPaths(value, projectRoot) {
if (typeof value === 'string' && value.startsWith(projectRoot)) {
return (0, Path_1.toPosixPath)(path_1.default.relative(projectRoot, value));
}
if (Array.isArray(value)) {
return value.map((item) => relativizeJsonPaths(item, projectRoot));
}
if (value && typeof value === 'object') {
return Object.fromEntries(Object.entries(value).map(([key, val]) => [key, relativizeJsonPaths(val, projectRoot)]));
}
return value;
}
function sortJson(json) {
if (Array.isArray(json)) {
return json.sort((a, b) => {
// Sort array items by their stringified value.
// We don't need the array to be sorted in meaningful way, just to be sorted in deterministic.
// E.g. `[{ b: '2' }, {}, { a: '3' }, null]` -> `[null, { a : '3' }, { b: '2' }, {}]`
// This result is not a perfect solution, but it's good enough for our use case.
const stringifiedA = stringifyJsonSorted(a);
const stringifiedB = stringifyJsonSorted(b);
if (stringifiedA < stringifiedB) {
return -1;
}
else if (stringifiedA > stringifiedB) {
return 1;
}
return 0;
});
}
if (json != null && typeof json === 'object') {
// Sort object items by keys
return Object.keys(json)
.sort()
.reduce((acc, key) => {
acc[key] = json[key];
return acc;
}, {});
}
// Return primitives
return json;
}
//# sourceMappingURL=Utils.js.map
@@ -0,0 +1 @@
{"version":3,"file":"Utils.js","sourceRoot":"","sources":["../../src/sourcer/Utils.ts"],"names":[],"mappings":"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"}
+41
View File
@@ -0,0 +1,41 @@
import { Minimatch, type MinimatchOptions } from 'minimatch';
/**
* Indicate the given `filePath` should be excluded by the `ignorePaths`.
*/
export declare function isIgnoredPath(filePath: string, ignorePaths: string[], minimatchOptions?: MinimatchOptions): boolean;
/**
* Prebuild match objects for `isIgnoredPathWithMatchObjects` calls.
*/
export declare function buildPathMatchObjects(paths: string[], minimatchOptions?: MinimatchOptions): Minimatch[];
/**
* Append a new ignore path to the given `matchObjects`.
*/
export declare function appendIgnorePath(matchObjects: Minimatch[], path: string, minimatchOptions?: MinimatchOptions): void;
/**
* Build an ignore match objects for directories based on the given `ignorePathMatchObjects`.
*/
export declare function buildDirMatchObjects(ignorePathMatchObjects: Minimatch[], minimatchOptions?: MinimatchOptions): Minimatch[];
/**
* Indicate the given `filePath` should be excluded by the prebuilt `matchObjects`.
*/
export declare function isIgnoredPathWithMatchObjects(filePath: string, matchObjects: Minimatch[]): boolean;
/**
* Normalize the given `filePath` to be used for matching against `ignorePaths`.
*
* @param filePath The file path to normalize.
* @param options.stripParentPrefix
* When people use fingerprint inside a monorepo, they may get source files from parent directories.
* However, minimatch '**' doesn't match the parent directories.
* We need to strip the `../` prefix to match the node_modules from parent directories.
*/
export declare function normalizeFilePath(filePath: string, options: {
stripParentPrefix?: boolean;
}): string;
/**
* Convert any platform-specific path to a POSIX path.
*/
export declare function toPosixPath(filePath: string): string;
/**
* Check if the given `filePath` exists.
*/
export declare function pathExistsAsync(filePath: string): Promise<boolean>;
+134
View File
@@ -0,0 +1,134 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isIgnoredPath = isIgnoredPath;
exports.buildPathMatchObjects = buildPathMatchObjects;
exports.appendIgnorePath = appendIgnorePath;
exports.buildDirMatchObjects = buildDirMatchObjects;
exports.isIgnoredPathWithMatchObjects = isIgnoredPathWithMatchObjects;
exports.normalizeFilePath = normalizeFilePath;
exports.toPosixPath = toPosixPath;
exports.pathExistsAsync = pathExistsAsync;
const promises_1 = __importDefault(require("fs/promises"));
const minimatch_1 = require("minimatch");
const node_process_1 = __importDefault(require("node:process"));
const path_1 = __importDefault(require("path"));
/**
* Indicate the given `filePath` should be excluded by the `ignorePaths`.
*/
function isIgnoredPath(filePath, ignorePaths, minimatchOptions = { dot: true }) {
const matchObjects = buildPathMatchObjects(ignorePaths, minimatchOptions);
return isIgnoredPathWithMatchObjects(filePath, matchObjects);
}
/**
* Prebuild match objects for `isIgnoredPathWithMatchObjects` calls.
*/
function buildPathMatchObjects(paths, minimatchOptions = { dot: true }) {
return paths.map((filePath) => new minimatch_1.Minimatch(filePath, minimatchOptions));
}
/**
* Append a new ignore path to the given `matchObjects`.
*/
function appendIgnorePath(matchObjects, path, minimatchOptions = { dot: true }) {
matchObjects.push(new minimatch_1.Minimatch(path, minimatchOptions));
}
/**
* Build an ignore match objects for directories based on the given `ignorePathMatchObjects`.
*/
function buildDirMatchObjects(ignorePathMatchObjects, minimatchOptions = { dot: true }) {
const dirIgnorePatterns = [];
const ignorePaths = ignorePathMatchObjects.filter((obj) => !obj.negate).map((obj) => obj.pattern);
const negatedIgnorePaths = ignorePathMatchObjects
.filter((obj) => obj.negate)
.map((obj) => obj.pattern);
// [0] Add positive patterns to dirIgnorePatterns
for (const pattern of ignorePaths) {
if (pattern.endsWith('/**/*')) {
// `/**/*` matches
dirIgnorePatterns.push(pattern.slice(0, -5));
}
else if (pattern.endsWith('/**')) {
// `/**` by default matches directories
dirIgnorePatterns.push(pattern.slice(0, -3));
}
else if (pattern.endsWith('/')) {
// `/` suffix matches directories
dirIgnorePatterns.push(pattern.slice(0, -1));
}
}
// [1] If there is a negate pattern in the same directory, we should remove the existing directory.
for (const pattern of negatedIgnorePaths) {
for (let i = 0; i < dirIgnorePatterns.length; ++i) {
const existingPattern = dirIgnorePatterns[i];
if (isSubDirectory(existingPattern, pattern)) {
dirIgnorePatterns.splice(i, 1);
}
}
}
return dirIgnorePatterns.map((pattern) => new minimatch_1.Minimatch(pattern, minimatchOptions));
}
/**
* Indicate the given `filePath` should be excluded by the prebuilt `matchObjects`.
*/
function isIgnoredPathWithMatchObjects(filePath, matchObjects) {
let result = false;
for (const minimatchObj of matchObjects) {
const stripParentPrefix = minimatchObj.pattern.startsWith('**/');
const normalizedFilePath = normalizeFilePath(filePath, { stripParentPrefix });
const currMatch = minimatchObj.match(normalizedFilePath);
if (minimatchObj.negate && result && !currMatch) {
// Special handler for negate (!pattern).
// As long as previous match result is true and not matched from the current negate pattern, we should early return.
return false;
}
if (!minimatchObj.negate) {
result ||= currMatch;
}
}
return result;
}
/**
* Returns true if `parent` is a parent directory of `child`.
*/
function isSubDirectory(parent, child) {
const relative = path_1.default.relative(parent, child);
return !relative.startsWith('..') && !path_1.default.isAbsolute(relative);
}
const STRIP_PARENT_PREFIX_REGEX = /^(\.\.\/)+/g;
/**
* Normalize the given `filePath` to be used for matching against `ignorePaths`.
*
* @param filePath The file path to normalize.
* @param options.stripParentPrefix
* When people use fingerprint inside a monorepo, they may get source files from parent directories.
* However, minimatch '**' doesn't match the parent directories.
* We need to strip the `../` prefix to match the node_modules from parent directories.
*/
function normalizeFilePath(filePath, options) {
if (options.stripParentPrefix) {
return filePath.replace(STRIP_PARENT_PREFIX_REGEX, '');
}
return filePath;
}
const REGEXP_REPLACE_SLASHES = /\\/g;
/**
* Convert any platform-specific path to a POSIX path.
*/
function toPosixPath(filePath) {
return node_process_1.default.platform === 'win32' ? filePath.replace(REGEXP_REPLACE_SLASHES, '/') : filePath;
}
/**
* Check if the given `filePath` exists.
*/
async function pathExistsAsync(filePath) {
try {
const stat = await promises_1.default.stat(filePath);
return stat.isFile() || stat.isDirectory();
}
catch {
return false;
}
}
//# sourceMappingURL=Path.js.map
@@ -0,0 +1 @@
{"version":3,"file":"Path.js","sourceRoot":"","sources":["../../src/utils/Path.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
export declare function nonNullish<TValue>(value: TValue | null | undefined): value is NonNullable<TValue>;
@@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.nonNullish = nonNullish;
function nonNullish(value) {
return value !== null && value !== undefined;
}
//# sourceMappingURL=Predicates.js.map
@@ -0,0 +1 @@
{"version":3,"file":"Predicates.js","sourceRoot":"","sources":["../../src/utils/Predicates.ts"],"names":[],"mappings":";;AAAA,gCAEC;AAFD,SAAgB,UAAU,CAAS,KAAgC;IACjE,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,9 @@
import type { NormalizedOptions } from '../Fingerprint.types';
/**
* Wrap a method and profile the time it takes to execute the method using `EXPO_PROFILE`.
* Works best with named functions (i.e. not arrow functions).
*
* @param fn function to profile.
* @param functionName optional name of the function to display in the profile output.
*/
export declare function profile<IArgs extends any[], T extends (...args: IArgs) => any>(options: NormalizedOptions, fn: T, functionName?: string): T;
+42
View File
@@ -0,0 +1,42 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.profile = profile;
const chalk_1 = __importDefault(require("chalk"));
/**
* Wrap a method and profile the time it takes to execute the method using `EXPO_PROFILE`.
* Works best with named functions (i.e. not arrow functions).
*
* @param fn function to profile.
* @param functionName optional name of the function to display in the profile output.
*/
function profile(options, fn, functionName = fn.name) {
if (!process.env['DEBUG'] || options.silent) {
return fn;
}
const name = chalk_1.default.dim(`⏱ [profile] ${functionName ?? 'unknown'}`);
return ((...args) => {
// Start the timer.
console.time(name);
// Invoke the method.
const results = fn(...args);
// If non-promise then return as-is.
if (!(results instanceof Promise)) {
console.timeEnd(name);
return results;
}
// Otherwise await to profile after the promise resolves.
return new Promise((resolve, reject) => {
results.then((results) => {
resolve(results);
console.timeEnd(name);
}, (reason) => {
reject(reason);
console.timeEnd(name);
});
});
});
}
//# sourceMappingURL=Profile.js.map
@@ -0,0 +1 @@
{"version":3,"file":"Profile.js","sourceRoot":"","sources":["../../src/utils/Profile.ts"],"names":[],"mappings":";;;;;AAWA,0BAsCC;AAjDD,kDAA0B;AAI1B;;;;;;GAMG;AACH,SAAgB,OAAO,CACrB,OAA0B,EAC1B,EAAK,EACL,eAAuB,EAAE,CAAC,IAAI;IAE9B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QAC5C,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,IAAI,GAAG,eAAK,CAAC,GAAG,CAAC,gBAAgB,YAAY,IAAI,SAAS,EAAE,CAAC,CAAC;IAEpE,OAAO,CAAC,CAAC,GAAG,IAAW,EAAE,EAAE;QACzB,mBAAmB;QACnB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEnB,qBAAqB;QACrB,MAAM,OAAO,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;QAE5B,oCAAoC;QACpC,IAAI,CAAC,CAAC,OAAO,YAAY,OAAO,CAAC,EAAE,CAAC;YAClC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACtB,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,yDAAyD;QACzD,OAAO,IAAI,OAAO,CAAyB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC7D,OAAO,CAAC,IAAI,CACV,CAAC,OAAO,EAAE,EAAE;gBACV,OAAO,CAAC,OAAO,CAAC,CAAC;gBACjB,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACxB,CAAC,EACD,CAAC,MAAM,EAAE,EAAE;gBACT,MAAM,CAAC,MAAM,CAAC,CAAC;gBACf,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACxB,CAAC,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAM,CAAC;AACV,CAAC"}
@@ -0,0 +1,6 @@
import { type SpawnOptions, type SpawnPromise, type SpawnResult } from '@expo/spawn-async';
interface SpawnWithIpcResult extends SpawnResult {
message: string;
}
export declare function spawnWithIpcAsync(command: string, args?: string[], options?: SpawnOptions): SpawnPromise<SpawnWithIpcResult>;
export {};
+29
View File
@@ -0,0 +1,29 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.spawnWithIpcAsync = spawnWithIpcAsync;
const spawn_async_1 = __importDefault(require("@expo/spawn-async"));
const node_assert_1 = __importDefault(require("node:assert"));
async function spawnWithIpcAsync(command, args, options
// @ts-expect-error: spawnAsync returns a customized Promise
) {
(0, node_assert_1.default)(options?.stdio == null, 'Cannot override stdio when using IPC');
const promise = (0, spawn_async_1.default)(command, args, {
...options,
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
});
const messageChunks = [];
const appendMessage = (message) => {
messageChunks.push(message);
};
promise.child.on('message', appendMessage);
const result = await promise;
promise.child.off('message', appendMessage);
return {
...result,
message: messageChunks.join(''),
};
}
//# sourceMappingURL=SpawnIPC.js.map
@@ -0,0 +1 @@
{"version":3,"file":"SpawnIPC.js","sourceRoot":"","sources":["../../src/utils/SpawnIPC.ts"],"names":[],"mappings":";;;;;AAWA,8CAwBC;AAnCD,oEAI2B;AAC3B,8DAAiC;AAM1B,KAAK,UAAU,iBAAiB,CACrC,OAAe,EACf,IAAe,EACf,OAAsB;AACtB,4DAA4D;;IAE5D,IAAA,qBAAM,EAAC,OAAO,EAAE,KAAK,IAAI,IAAI,EAAE,sCAAsC,CAAC,CAAC;IAEvE,MAAM,OAAO,GAAG,IAAA,qBAAU,EAAC,OAAO,EAAE,IAAI,EAAE;QACxC,GAAG,OAAO;QACV,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC;KACvC,CAAC,CAAC;IAEH,MAAM,aAAa,GAAa,EAAE,CAAC;IACnC,MAAM,aAAa,GAAG,CAAC,OAAY,EAAE,EAAE;QACrC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC,CAAC;IACF,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC;IAC7B,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IAC5C,OAAO;QACL,GAAG,MAAM;QACT,OAAO,EAAE,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;KAChC,CAAC;AACJ,CAAC"}
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env node
export type Command = (argv?: string[]) => void;
+106
View File
@@ -0,0 +1,106 @@
#!/usr/bin/env node
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const arg_1 = __importDefault(require("arg"));
const chalk_1 = __importDefault(require("chalk"));
const debug_1 = __importDefault(require("debug"));
const getenv_1 = require("getenv");
const runLegacyCLIAsync_1 = require("./runLegacyCLIAsync");
const errors_1 = require("./utils/errors");
const Log = __importStar(require("./utils/log"));
// Setup before requiring `debug`.
if ((0, getenv_1.boolish)('EXPO_DEBUG', false)) {
debug_1.default.enable('@expo/fingerprint:*');
}
else if (debug_1.default.enabled('@expo/fingerprint:')) {
process.env.EXPO_DEBUG = '1';
}
const commands = {
// Add a new command here
'fingerprint:generate': () => import('./commands/generateFingerprint.js').then((i) => i.generateFingerprintAsync),
'fingerprint:diff': () => import('./commands/diffFingerprints.js').then((i) => i.diffFingerprintsAsync),
};
const args = (0, arg_1.default)({
// Types
'--version': Boolean,
'--help': Boolean,
// Aliases
'-h': '--help',
}, {
permissive: true,
});
if (args['--version']) {
// Version is added in the build script.
const packageJSON = require('../../package.json');
console.log(packageJSON.version);
process.exit(0);
}
const command = args._[0];
const commandArgs = args._.slice(1);
// Handle `--help` flag
if ((args['--help'] && !command) || !command) {
Log.exit((0, chalk_1.default) `
{bold Usage}
{dim $} npx @expo/fingerprint <command>
{bold Commands}
${Object.keys(commands).sort().join(', ')}
{bold Options}
--help, -h Displays this message
For more information run a command with the --help flag
{dim $} npx @expo/fingerprint fingerprint:generate --help
`, 0);
}
// Push the help flag to the subcommand args.
if (args['--help']) {
commandArgs.push('--help');
}
// Install exit hooks
process.on('SIGINT', () => process.exit(0));
process.on('SIGTERM', () => process.exit(0));
if (!(command in commands)) {
(0, runLegacyCLIAsync_1.runLegacyCLIAsync)(args._).catch(errors_1.logCmdError);
}
else {
commands[command]()
.then((exec) => exec(commandArgs))
.catch(errors_1.logCmdError);
}
@@ -0,0 +1,2 @@
import { Command } from '../cli';
export declare const diffFingerprintsAsync: Command;
@@ -0,0 +1,79 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.diffFingerprintsAsync = void 0;
const chalk_1 = __importDefault(require("chalk"));
const index_1 = require("../../../build/index");
const args_1 = require("../utils/args");
const errors_1 = require("../utils/errors");
const Log = __importStar(require("../utils/log"));
const readFingerprintFileAsync_1 = __importDefault(require("../utils/readFingerprintFileAsync"));
const diffFingerprintsAsync = async (argv) => {
const args = (0, args_1.assertArgs)({
// Types
'--help': Boolean,
// Aliases
'-h': '--help',
}, argv ?? []);
if (args['--help']) {
Log.exit((0, chalk_1.default) `
{bold Description}
Diff two fingerprints
{bold Usage}
{dim $} npx @expo/fingerprint fingerprint:diff <fingerprintFile1> <fingerprintFile2>
Options
-h, --help Output usage information
`, 0);
}
const fingerprintFile1 = (0, args_1.getFileArgumentAtIndex)(args, 0);
const fingerprintFile2 = (0, args_1.getFileArgumentAtIndex)(args, 1);
const [fingerprint1ToDiff, fingerprint2ToDiff] = await Promise.all([
(0, readFingerprintFileAsync_1.default)(fingerprintFile1),
(0, readFingerprintFileAsync_1.default)(fingerprintFile2),
]);
try {
const diff = (0, index_1.diffFingerprints)(fingerprint1ToDiff, fingerprint2ToDiff);
console.log(JSON.stringify(diff, null, 2));
}
catch (e) {
throw new errors_1.CommandError(e.message);
}
};
exports.diffFingerprintsAsync = diffFingerprintsAsync;
@@ -0,0 +1,2 @@
import { Command } from '../cli';
export declare const generateFingerprintAsync: Command;
@@ -0,0 +1,131 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateFingerprintAsync = void 0;
const chalk_1 = __importDefault(require("chalk"));
const getenv_1 = require("getenv");
const index_1 = require("../../../build/index");
const args_1 = require("../utils/args");
const errors_1 = require("../utils/errors");
const Log = __importStar(require("../utils/log"));
const withConsoleDisabledAsync_1 = require("../utils/withConsoleDisabledAsync");
const generateFingerprintAsync = async (argv) => {
const args = (0, args_1.assertArgs)({
// Types
'--help': Boolean,
'--platform': [String],
'--concurrent-io-limit': Number,
'--hash-algorithm': String,
'--ignore-path': [String],
'--source-skips': Number,
'--debug': Boolean,
// Aliases
'-h': '--help',
}, argv ?? []);
if (args['--help']) {
Log.exit((0, chalk_1.default) `
{bold Description}
Generate fingerprint for a project
{bold Usage}
{dim $} npx @expo/fingerprint fingerprint:generate
Options
--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
-h, --help Output usage information
`, 0);
}
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,
...(platforms ? { platforms } : null),
...(concurrentIoLimit ? { concurrentIoLimit } : null),
...(hashAlgorithm ? { hashAlgorithm } : null),
...(ignorePaths ? { ignorePaths } : null),
...(sourceSkips ? { sourceSkips } : null),
};
const projectRoot = (0, args_1.getProjectRoot)(args);
const result = await (0, withConsoleDisabledAsync_1.withConsoleDisabledAsync)(async () => {
try {
return await (0, index_1.createFingerprintAsync)(projectRoot, options);
}
catch (e) {
throw new errors_1.CommandError(e.message);
}
});
console.log(JSON.stringify(result));
};
exports.generateFingerprintAsync = generateFingerprintAsync;
@@ -0,0 +1 @@
export declare function runLegacyCLIAsync(args: string[]): Promise<void>;
@@ -0,0 +1,45 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.runLegacyCLIAsync = runLegacyCLIAsync;
const readFingerprintFileAsync_js_1 = __importDefault(require("./utils/readFingerprintFileAsync.js"));
const index_js_1 = require("../../build/index.js");
async function runLegacyCLIAsync(args) {
if (args.length !== 1 && args.length !== 2 && args.length !== 3) {
console.log(`Usage: npx @expo/fingerprint <projectRoot> [fingerprintFile1ToDiff] [fingerprintFile2ToDiff]`);
process.exit(1);
}
const projectRoot = args[0];
const fingerprintFile1ToDiff = args[1];
const fingerprintFile2ToDiff = args[2];
const [fingerprint1ToDiff, fingerprint2ToDiff] = await Promise.all([
fingerprintFile1ToDiff ? (0, readFingerprintFileAsync_js_1.default)(fingerprintFile1ToDiff) : null,
fingerprintFile2ToDiff ? (0, readFingerprintFileAsync_js_1.default)(fingerprintFile2ToDiff) : null,
]);
const options = {
debug: !!process.env.DEBUG,
useRNCoreAutolinkingFromExpo: process.env.USE_RNCORE_AUTOLINKING_FROM_EXPO
? ['1', 'true'].includes(process.env.USE_RNCORE_AUTOLINKING_FROM_EXPO)
: undefined,
};
try {
if (fingerprint1ToDiff && fingerprint2ToDiff) {
const diff = (0, index_js_1.diffFingerprints)(fingerprint1ToDiff, fingerprint2ToDiff);
console.log(JSON.stringify(diff, null, 2));
}
else if (fingerprint1ToDiff) {
const diff = await (0, index_js_1.diffFingerprintChangesAsync)(fingerprint1ToDiff, projectRoot, options);
console.log(JSON.stringify(diff, null, 2));
}
else {
const fingerprint = await (0, index_js_1.createFingerprintAsync)(projectRoot, options);
console.log(JSON.stringify(fingerprint, null, 2));
}
}
catch (e) {
console.error('Uncaught Error', e);
process.exit(1);
}
}
@@ -0,0 +1,17 @@
import arg from 'arg';
/**
* Parse the first argument as a project directory.
*
* @returns valid project directory.
*/
export declare function getProjectRoot(args: arg.Result<arg.Spec>): string;
export declare function getFileArgumentAtIndex(args: arg.Result<arg.Spec>, index: number): string;
/**
* Parse args and assert unknown options.
*
* @param schema the `args` schema for parsing the command line arguments.
* @param argv extra strings
* @returns processed args object.
*/
export declare function assertArgs(schema: arg.Spec, argv: string[]): arg.Result<arg.Spec>;
export declare function requireArg(args: arg.Result<arg.Spec>, name: any): any;
+94
View File
@@ -0,0 +1,94 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getProjectRoot = getProjectRoot;
exports.getFileArgumentAtIndex = getFileArgumentAtIndex;
exports.assertArgs = assertArgs;
exports.requireArg = requireArg;
// Common utilities for interacting with `args` library.
// These functions should be used by every command.
const arg_1 = __importDefault(require("arg"));
const fs_1 = require("fs");
const path_1 = require("path");
const Log = __importStar(require("./log"));
/**
* Parse the first argument as a project directory.
*
* @returns valid project directory.
*/
function getProjectRoot(args) {
const projectRoot = (0, path_1.resolve)(args._[0] || '.');
if (!(0, fs_1.existsSync)(projectRoot)) {
Log.exit(`Invalid project root: ${projectRoot}`);
}
return projectRoot;
}
function getFileArgumentAtIndex(args, index) {
const path = (0, path_1.resolve)(args._[index]);
if (!(0, fs_1.existsSync)(path)) {
Log.exit(`Invalid file: ${path}`);
}
return path;
}
/**
* Parse args and assert unknown options.
*
* @param schema the `args` schema for parsing the command line arguments.
* @param argv extra strings
* @returns processed args object.
*/
function assertArgs(schema, argv) {
try {
return (0, arg_1.default)(schema, { argv });
}
catch (error) {
// Ensure unknown options are handled the same way.
if (error.code === 'ARG_UNKNOWN_OPTION') {
Log.exit(error.message, 1);
}
// Otherwise rethrow the error.
throw error;
}
}
function requireArg(args, name) {
const value = args[name];
if (value === undefined || value === null) {
Log.exit(`${name} must be provided`, 1);
}
return value;
}
@@ -0,0 +1,10 @@
/**
* General error, formatted as a message in red text when caught by expo-cli (no stack trace is printed). Should be used in favor of `log.error()` in most cases.
*/
export declare class CommandError extends Error {
code: string;
name: string;
readonly isCommandError = true;
constructor(code: string, message?: string);
}
export declare function logCmdError(error: any): never;
@@ -0,0 +1,41 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CommandError = void 0;
exports.logCmdError = logCmdError;
const assert_1 = require("assert");
const chalk_1 = __importDefault(require("chalk"));
const log_1 = require("./log");
const ERROR_PREFIX = 'Error: ';
/**
* General error, formatted as a message in red text when caught by expo-cli (no stack trace is printed). Should be used in favor of `log.error()` in most cases.
*/
class CommandError extends Error {
code;
name = 'CommandError';
isCommandError = true;
constructor(code, message = '') {
super('');
this.code = code;
// If e.toString() was called to get `message` we don't want it to look
// like "Error: Error:".
if (message.startsWith(ERROR_PREFIX)) {
message = message.substring(ERROR_PREFIX.length);
}
this.message = message || code;
}
}
exports.CommandError = CommandError;
function logCmdError(error) {
if (!(error instanceof Error)) {
throw error;
}
if (error instanceof CommandError || error instanceof assert_1.AssertionError) {
// Print the stack trace in debug mode only.
(0, log_1.exit)(error);
}
const errorDetails = error.stack ? '\n' + chalk_1.default.gray(error.stack) : '';
(0, log_1.exit)(chalk_1.default.red(error.toString()) + errorDetails);
}
+11
View File
@@ -0,0 +1,11 @@
export declare function time(label?: string): void;
export declare function timeEnd(label?: string): void;
export declare function error(...message: string[]): void;
/** Print an error and provide additional info (the stack trace) in debug mode. */
export declare function exception(e: Error): void;
export declare function warn(...message: string[]): void;
export declare function log(...message: string[]): void;
/** Clear the terminal of all text. */
export declare function clear(): void;
/** Log a message and exit the current process. If the `code` is non-zero then `console.error` will be used instead of `console.log`. */
export declare function exit(message: string | Error, code?: number): never;
+53
View File
@@ -0,0 +1,53 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.time = time;
exports.timeEnd = timeEnd;
exports.error = error;
exports.exception = exception;
exports.warn = warn;
exports.log = log;
exports.clear = clear;
exports.exit = exit;
const chalk_1 = __importDefault(require("chalk"));
function time(label) {
console.time(label);
}
function timeEnd(label) {
console.timeEnd(label);
}
function error(...message) {
console.error(...message);
}
/** Print an error and provide additional info (the stack trace) in debug mode. */
function exception(e) {
error(chalk_1.default.red(e.toString()) + (process.env.EXPO_DEBUG ? '\n' + chalk_1.default.gray(e.stack) : ''));
}
function warn(...message) {
console.warn(...message.map((value) => chalk_1.default.yellow(value)));
}
function log(...message) {
console.log(...message);
}
/** Clear the terminal of all text. */
function clear() {
process.stdout.write(process.platform === 'win32' ? '\x1B[2J\x1B[0f' : '\x1B[2J\x1B[3J\x1B[H');
}
/** Log a message and exit the current process. If the `code` is non-zero then `console.error` will be used instead of `console.log`. */
function exit(message, code = 1) {
if (message instanceof Error) {
exception(message);
process.exit(code);
}
if (message) {
if (code === 0) {
log(message);
}
else {
error(message);
}
}
process.exit(code);
}
@@ -0,0 +1,2 @@
import { Fingerprint } from '../../../build/Fingerprint.types';
export default function readFingerprintFileAsync(path: string): Promise<Fingerprint>;
@@ -0,0 +1,15 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = readFingerprintFileAsync;
const promises_1 = __importDefault(require("fs/promises"));
async function readFingerprintFileAsync(path) {
try {
return JSON.parse(await promises_1.default.readFile(path, 'utf-8'));
}
catch (e) {
throw new Error(`Unable to read fingerprint file ${path}: ${e.message}`);
}
}
@@ -0,0 +1 @@
export declare function withConsoleDisabledAsync<T>(block: () => Promise<T>): Promise<T>;
@@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.withConsoleDisabledAsync = withConsoleDisabledAsync;
async function withConsoleDisabledAsync(block) {
const loggingFunctions = {
log: console.log,
warn: console.warn,
error: console.error,
};
// Disable logging for this command since the only thing printed to stdout should be the JSON output.
console.log = function () { };
console.warn = function () { };
console.error = function () { };
try {
return await block();
}
finally {
// Re-enable logging functions for testing.
console.log = loggingFunctions.log;
console.warn = loggingFunctions.warn;
console.error = loggingFunctions.error;
}
}
+1
View File
@@ -0,0 +1 @@
../semver/bin/semver.js
@@ -0,0 +1,15 @@
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
@@ -0,0 +1,664 @@
semver(1) -- The semantic versioner for npm
===========================================
## Install
```bash
npm install semver
````
## Usage
As a node module:
```js
const semver = require('semver')
semver.valid('1.2.3') // '1.2.3'
semver.valid('a.b.c') // null
semver.clean(' =v1.2.3 ') // '1.2.3'
semver.satisfies('1.2.3', '1.x || >=2.5.0 || 5.0.0 - 7.2.3') // true
semver.gt('1.2.3', '9.8.7') // false
semver.lt('1.2.3', '9.8.7') // true
semver.minVersion('>=1.0.0') // '1.0.0'
semver.valid(semver.coerce('v2')) // '2.0.0'
semver.valid(semver.coerce('42.6.7.9.3-alpha')) // '42.6.7'
```
You can also just load the module for the function that you care about if
you'd like to minimize your footprint.
```js
// load the whole API at once in a single object
const semver = require('semver')
// or just load the bits you need
// all of them listed here, just pick and choose what you want
// classes
const SemVer = require('semver/classes/semver')
const Comparator = require('semver/classes/comparator')
const Range = require('semver/classes/range')
// functions for working with versions
const semverParse = require('semver/functions/parse')
const semverValid = require('semver/functions/valid')
const semverClean = require('semver/functions/clean')
const semverInc = require('semver/functions/inc')
const semverDiff = require('semver/functions/diff')
const semverMajor = require('semver/functions/major')
const semverMinor = require('semver/functions/minor')
const semverPatch = require('semver/functions/patch')
const semverPrerelease = require('semver/functions/prerelease')
const semverCompare = require('semver/functions/compare')
const semverRcompare = require('semver/functions/rcompare')
const semverCompareLoose = require('semver/functions/compare-loose')
const semverCompareBuild = require('semver/functions/compare-build')
const semverSort = require('semver/functions/sort')
const semverRsort = require('semver/functions/rsort')
// low-level comparators between versions
const semverGt = require('semver/functions/gt')
const semverLt = require('semver/functions/lt')
const semverEq = require('semver/functions/eq')
const semverNeq = require('semver/functions/neq')
const semverGte = require('semver/functions/gte')
const semverLte = require('semver/functions/lte')
const semverCmp = require('semver/functions/cmp')
const semverCoerce = require('semver/functions/coerce')
// working with ranges
const semverSatisfies = require('semver/functions/satisfies')
const semverMaxSatisfying = require('semver/ranges/max-satisfying')
const semverMinSatisfying = require('semver/ranges/min-satisfying')
const semverToComparators = require('semver/ranges/to-comparators')
const semverMinVersion = require('semver/ranges/min-version')
const semverValidRange = require('semver/ranges/valid')
const semverOutside = require('semver/ranges/outside')
const semverGtr = require('semver/ranges/gtr')
const semverLtr = require('semver/ranges/ltr')
const semverIntersects = require('semver/ranges/intersects')
const semverSimplifyRange = require('semver/ranges/simplify')
const semverRangeSubset = require('semver/ranges/subset')
```
As a command-line utility:
```
$ semver -h
A JavaScript implementation of the https://semver.org/ specification
Copyright Isaac Z. Schlueter
Usage: semver [options] <version> [<version> [...]]
Prints valid versions sorted by SemVer precedence
Options:
-r --range <range>
Print versions that match the specified range.
-i --increment [<level>]
Increment a version by the specified level. Level can
be one of: major, minor, patch, premajor, preminor,
prepatch, prerelease, or release. Default level is 'patch'.
Only one version may be specified.
--preid <identifier>
Identifier to be used to prefix premajor, preminor,
prepatch or prerelease version increments.
-l --loose
Interpret versions and ranges loosely
-n <0|1>
This is the base to be used for the prerelease identifier.
-p --include-prerelease
Always include prerelease versions in range matching
-c --coerce
Coerce a string into SemVer if possible
(does not imply --loose)
--rtl
Coerce version strings right to left
--ltr
Coerce version strings left to right (default)
Program exits successfully if any valid version satisfies
all supplied ranges, and prints all satisfying versions.
If no satisfying versions are found, then exits failure.
Versions are printed in ascending order, so supplying
multiple versions to the utility will just sort them.
```
## Versions
A "version" is described by the `v2.0.0` specification found at
<https://semver.org/>.
A leading `"="` or `"v"` character is stripped off and ignored.
Support for stripping a leading "v" is kept for compatibility with `v1.0.0` of the SemVer
specification but should not be used anymore.
## Ranges
A `version range` is a set of `comparators` that specify versions
that satisfy the range.
A `comparator` is composed of an `operator` and a `version`. The set
of primitive `operators` is:
* `<` Less than
* `<=` Less than or equal to
* `>` Greater than
* `>=` Greater than or equal to
* `=` Equal. If no operator is specified, then equality is assumed,
so this operator is optional but MAY be included.
For example, the comparator `>=1.2.7` would match the versions
`1.2.7`, `1.2.8`, `2.5.3`, and `1.3.9`, but not the versions `1.2.6`
or `1.1.0`. The comparator `>1` is equivalent to `>=2.0.0` and
would match the versions `2.0.0` and `3.1.0`, but not the versions
`1.0.1` or `1.1.0`.
Comparators can be joined by whitespace to form a `comparator set`,
which is satisfied by the **intersection** of all of the comparators
it includes.
A range is composed of one or more comparator sets, joined by `||`. A
version matches a range if and only if every comparator in at least
one of the `||`-separated comparator sets is satisfied by the version.
For example, the range `>=1.2.7 <1.3.0` would match the versions
`1.2.7`, `1.2.8`, and `1.2.99`, but not the versions `1.2.6`, `1.3.0`,
or `1.1.0`.
The range `1.2.7 || >=1.2.9 <2.0.0` would match the versions `1.2.7`,
`1.2.9`, and `1.4.6`, but not the versions `1.2.8` or `2.0.0`.
### Prerelease Tags
If a version has a prerelease tag (for example, `1.2.3-alpha.3`) then
it will only be allowed to satisfy comparator sets if at least one
comparator with the same `[major, minor, patch]` tuple also has a
prerelease tag.
For example, the range `>1.2.3-alpha.3` would be allowed to match the
version `1.2.3-alpha.7`, but it would *not* be satisfied by
`3.4.5-alpha.9`, even though `3.4.5-alpha.9` is technically "greater
than" `1.2.3-alpha.3` according to the SemVer sort rules. The version
range only accepts prerelease tags on the `1.2.3` version.
Version `3.4.5` *would* satisfy the range because it does not have a
prerelease flag, and `3.4.5` is greater than `1.2.3-alpha.7`.
The purpose of this behavior is twofold. First, prerelease versions
frequently are updated very quickly, and contain many breaking changes
that are (by the author's design) not yet fit for public consumption.
Therefore, by default, they are excluded from range-matching
semantics.
Second, a user who has opted into using a prerelease version has
indicated the intent to use *that specific* set of
alpha/beta/rc versions. By including a prerelease tag in the range,
the user is indicating that they are aware of the risk. However, it
is still not appropriate to assume that they have opted into taking a
similar risk on the *next* set of prerelease versions.
Note that this behavior can be suppressed (treating all prerelease
versions as if they were normal versions, for range-matching)
by setting the `includePrerelease` flag on the options
object to any
[functions](https://github.com/npm/node-semver#functions) that do
range matching.
#### Prerelease Identifiers
The method `.inc` takes an additional `identifier` string argument that
will append the value of the string as a prerelease identifier:
```javascript
semver.inc('1.2.3', 'prerelease', 'beta')
// '1.2.4-beta.0'
```
command-line example:
```bash
$ semver 1.2.3 -i prerelease --preid beta
1.2.4-beta.0
```
Which then can be used to increment further:
```bash
$ semver 1.2.4-beta.0 -i prerelease
1.2.4-beta.1
```
To get out of the prerelease phase, use the `release` option:
```bash
$ semver 1.2.4-beta.1 -i release
1.2.4
```
#### Prerelease Identifier Base
The method `.inc` takes an optional parameter 'identifierBase' string
that will let you let your prerelease number as zero-based or one-based.
Set to `false` to omit the prerelease number altogether.
If you do not specify this parameter, it will default to zero-based.
```javascript
semver.inc('1.2.3', 'prerelease', 'beta', '1')
// '1.2.4-beta.1'
```
```javascript
semver.inc('1.2.3', 'prerelease', 'beta', false)
// '1.2.4-beta'
```
command-line example:
```bash
$ semver 1.2.3 -i prerelease --preid beta -n 1
1.2.4-beta.1
```
```bash
$ semver 1.2.3 -i prerelease --preid beta -n false
1.2.4-beta
```
### Advanced Range Syntax
Advanced range syntax desugars to primitive comparators in
deterministic ways.
Advanced ranges may be combined in the same way as primitive
comparators using white space or `||`.
#### Hyphen Ranges `X.Y.Z - A.B.C`
Specifies an inclusive set.
* `1.2.3 - 2.3.4` := `>=1.2.3 <=2.3.4`
If a partial version is provided as the first version in the inclusive
range, then the missing pieces are replaced with zeroes.
* `1.2 - 2.3.4` := `>=1.2.0 <=2.3.4`
If a partial version is provided as the second version in the
inclusive range, then all versions that start with the supplied parts
of the tuple are accepted, but nothing that would be greater than the
provided tuple parts.
* `1.2.3 - 2.3` := `>=1.2.3 <2.4.0-0`
* `1.2.3 - 2` := `>=1.2.3 <3.0.0-0`
#### X-Ranges `1.2.x` `1.X` `1.2.*` `*`
Any of `X`, `x`, or `*` may be used to "stand in" for one of the
numeric values in the `[major, minor, patch]` tuple.
* `*` := `>=0.0.0` (Any non-prerelease version satisfies, unless
`includePrerelease` is specified, in which case any version at all
satisfies)
* `1.x` := `>=1.0.0 <2.0.0-0` (Matching major version)
* `1.2.x` := `>=1.2.0 <1.3.0-0` (Matching major and minor versions)
A partial version range is treated as an X-Range, so the special
character is in fact optional.
* `""` (empty string) := `*` := `>=0.0.0`
* `1` := `1.x.x` := `>=1.0.0 <2.0.0-0`
* `1.2` := `1.2.x` := `>=1.2.0 <1.3.0-0`
#### Tilde Ranges `~1.2.3` `~1.2` `~1`
Allows patch-level changes if a minor version is specified on the
comparator. Allows minor-level changes if not.
* `~1.2.3` := `>=1.2.3 <1.(2+1).0` := `>=1.2.3 <1.3.0-0`
* `~1.2` := `>=1.2.0 <1.(2+1).0` := `>=1.2.0 <1.3.0-0` (Same as `1.2.x`)
* `~1` := `>=1.0.0 <(1+1).0.0` := `>=1.0.0 <2.0.0-0` (Same as `1.x`)
* `~0.2.3` := `>=0.2.3 <0.(2+1).0` := `>=0.2.3 <0.3.0-0`
* `~0.2` := `>=0.2.0 <0.(2+1).0` := `>=0.2.0 <0.3.0-0` (Same as `0.2.x`)
* `~0` := `>=0.0.0 <(0+1).0.0` := `>=0.0.0 <1.0.0-0` (Same as `0.x`)
* `~1.2.3-beta.2` := `>=1.2.3-beta.2 <1.3.0-0` Note that prereleases in
the `1.2.3` version will be allowed, if they are greater than or
equal to `beta.2`. So, `1.2.3-beta.4` would be allowed, but
`1.2.4-beta.2` would not, because it is a prerelease of a
different `[major, minor, patch]` tuple.
#### Caret Ranges `^1.2.3` `^0.2.5` `^0.0.4`
Allows changes that do not modify the left-most non-zero element in the
`[major, minor, patch]` tuple. In other words, this allows patch and
minor updates for versions `1.0.0` and above, patch updates for
versions `0.X >=0.1.0`, and *no* updates for versions `0.0.X`.
Many authors treat a `0.x` version as if the `x` were the major
"breaking-change" indicator.
Caret ranges are ideal when an author may make breaking changes
between `0.2.4` and `0.3.0` releases, which is a common practice.
However, it presumes that there will *not* be breaking changes between
`0.2.4` and `0.2.5`. It allows for changes that are presumed to be
additive (but non-breaking), according to commonly observed practices.
* `^1.2.3` := `>=1.2.3 <2.0.0-0`
* `^0.2.3` := `>=0.2.3 <0.3.0-0`
* `^0.0.3` := `>=0.0.3 <0.0.4-0`
* `^1.2.3-beta.2` := `>=1.2.3-beta.2 <2.0.0-0` Note that prereleases in
the `1.2.3` version will be allowed, if they are greater than or
equal to `beta.2`. So, `1.2.3-beta.4` would be allowed, but
`1.2.4-beta.2` would not, because it is a prerelease of a
different `[major, minor, patch]` tuple.
* `^0.0.3-beta` := `>=0.0.3-beta <0.0.4-0` Note that prereleases in the
`0.0.3` version *only* will be allowed, if they are greater than or
equal to `beta`. So, `0.0.3-pr.2` would be allowed.
When parsing caret ranges, a missing `patch` value desugars to the
number `0`, but will allow flexibility within that value, even if the
major and minor versions are both `0`.
* `^1.2.x` := `>=1.2.0 <2.0.0-0`
* `^0.0.x` := `>=0.0.0 <0.1.0-0`
* `^0.0` := `>=0.0.0 <0.1.0-0`
A missing `minor` and `patch` values will desugar to zero, but also
allow flexibility within those values, even if the major version is
zero.
* `^1.x` := `>=1.0.0 <2.0.0-0`
* `^0.x` := `>=0.0.0 <1.0.0-0`
### Range Grammar
Putting all this together, here is a Backus-Naur grammar for ranges,
for the benefit of parser authors:
```bnf
range-set ::= range ( logical-or range ) *
logical-or ::= ( ' ' ) * '||' ( ' ' ) *
range ::= hyphen | simple ( ' ' simple ) * | ''
hyphen ::= partial ' - ' partial
simple ::= primitive | partial | tilde | caret
primitive ::= ( '<' | '>' | '>=' | '<=' | '=' ) partial
partial ::= xr ( '.' xr ( '.' xr qualifier ? )? )?
xr ::= 'x' | 'X' | '*' | nr
nr ::= '0' | ['1'-'9'] ( ['0'-'9'] ) *
tilde ::= '~' partial
caret ::= '^' partial
qualifier ::= ( '-' pre )? ( '+' build )?
pre ::= parts
build ::= parts
parts ::= part ( '.' part ) *
part ::= nr | [-0-9A-Za-z]+
```
## Functions
All methods and classes take a final `options` object argument. All
options in this object are `false` by default. The options supported
are:
- `loose`: Be more forgiving about not-quite-valid semver strings.
(Any resulting output will always be 100% strict compliant, of
course.) For backwards compatibility reasons, if the `options`
argument is a boolean value instead of an object, it is interpreted
to be the `loose` param.
- `includePrerelease`: Set to suppress the [default
behavior](https://github.com/npm/node-semver#prerelease-tags) of
excluding prerelease tagged versions from ranges unless they are
explicitly opted into.
Strict-mode Comparators and Ranges will be strict about the SemVer
strings that they parse.
* `valid(v)`: Return the parsed version, or null if it's not valid.
* `inc(v, releaseType, options, identifier, identifierBase)`:
Return the version incremented by the release
type (`major`, `premajor`, `minor`, `preminor`, `patch`,
`prepatch`, `prerelease`, or `release`), or null if it's not valid
* `premajor` in one call will bump the version up to the next major
version and down to a prerelease of that major version.
`preminor`, and `prepatch` work the same way.
* If called from a non-prerelease version, `prerelease` will work the
same as `prepatch`. It increments the patch version and then makes a
prerelease. If the input version is already a prerelease it simply
increments it.
* `release` will remove any prerelease part of the version.
* `identifier` can be used to prefix `premajor`, `preminor`,
`prepatch`, or `prerelease` version increments. `identifierBase`
is the base to be used for the `prerelease` identifier.
* `prerelease(v)`: Returns an array of prerelease components, or null
if none exist. Example: `prerelease('1.2.3-alpha.1') -> ['alpha', 1]`
* `major(v)`: Return the major version number.
* `minor(v)`: Return the minor version number.
* `patch(v)`: Return the patch version number.
* `intersects(r1, r2, loose)`: Return true if the two supplied ranges
or comparators intersect.
* `parse(v)`: Attempt to parse a string as a semantic version, returning either
a `SemVer` object or `null`.
### Comparison
* `gt(v1, v2)`: `v1 > v2`
* `gte(v1, v2)`: `v1 >= v2`
* `lt(v1, v2)`: `v1 < v2`
* `lte(v1, v2)`: `v1 <= v2`
* `eq(v1, v2)`: `v1 == v2` This is true if they're logically equivalent,
even if they're not the same string. You already know how to
compare strings.
* `neq(v1, v2)`: `v1 != v2` The opposite of `eq`.
* `cmp(v1, comparator, v2)`: Pass in a comparison string, and it'll call
the corresponding function above. `"==="` and `"!=="` do simple
string comparison, but are included for completeness. Throws if an
invalid comparison string is provided.
* `compare(v1, v2)`: Return `0` if `v1 == v2`, or `1` if `v1` is greater, or `-1` if
`v2` is greater. Sorts in ascending order if passed to `Array.sort()`.
* `rcompare(v1, v2)`: The reverse of `compare`. Sorts an array of versions
in descending order when passed to `Array.sort()`.
* `compareBuild(v1, v2)`: The same as `compare` but considers `build` when two versions
are equal. Sorts in ascending order if passed to `Array.sort()`.
* `compareLoose(v1, v2)`: Short for `compare(v1, v2, { loose: true })`.
* `diff(v1, v2)`: Returns the difference between two versions by the release type
(`major`, `premajor`, `minor`, `preminor`, `patch`, `prepatch`, or `prerelease`),
or null if the versions are the same.
### Sorting
* `sort(versions)`: Returns a sorted array of versions based on the `compareBuild`
function.
* `rsort(versions)`: The reverse of `sort`. Returns an array of versions based on
the `compareBuild` function in descending order.
### Comparators
* `intersects(comparator)`: Return true if the comparators intersect
### Ranges
* `validRange(range)`: Return the valid range or null if it's not valid.
* `satisfies(version, range)`: Return true if the version satisfies the
range.
* `maxSatisfying(versions, range)`: Return the highest version in the list
that satisfies the range, or `null` if none of them do.
* `minSatisfying(versions, range)`: Return the lowest version in the list
that satisfies the range, or `null` if none of them do.
* `minVersion(range)`: Return the lowest version that can match
the given range.
* `gtr(version, range)`: Return `true` if the version is greater than all the
versions possible in the range.
* `ltr(version, range)`: Return `true` if the version is less than all the
versions possible in the range.
* `outside(version, range, hilo)`: Return true if the version is outside
the bounds of the range in either the high or low direction. The
`hilo` argument must be either the string `'>'` or `'<'`. (This is
the function called by `gtr` and `ltr`.)
* `intersects(range)`: Return true if any of the range comparators intersect.
* `simplifyRange(versions, range)`: Return a "simplified" range that
matches the same items in the `versions` list as the range specified. Note
that it does *not* guarantee that it would match the same versions in all
cases, only for the set of versions provided. This is useful when
generating ranges by joining together multiple versions with `||`
programmatically, to provide the user with something a bit more
ergonomic. If the provided range is shorter in string-length than the
generated range, then that is returned.
* `subset(subRange, superRange)`: Return `true` if the `subRange` range is
entirely contained by the `superRange` range.
Note that, since ranges may be non-contiguous, a version might not be
greater than a range, less than a range, *or* satisfy a range! For
example, the range `1.2 <1.2.9 || >2.0.0` would have a hole from `1.2.9`
until `2.0.0`, so version `1.2.10` would not be greater than the
range (because `2.0.1` satisfies, which is higher), nor less than the
range (since `1.2.8` satisfies, which is lower), and it also does not
satisfy the range.
If you want to know if a version satisfies or does not satisfy a
range, use the `satisfies(version, range)` function.
### Coercion
* `coerce(version, options)`: Coerces a string to semver if possible
This aims to provide a very forgiving translation of a non-semver string to
semver. It looks for the first digit in a string and consumes all
remaining characters which satisfy at least a partial semver (e.g., `1`,
`1.2`, `1.2.3`) up to the max permitted length (256 characters). Longer
versions are simply truncated (`4.6.3.9.2-alpha2` becomes `4.6.3`). All
surrounding text is simply ignored (`v3.4 replaces v3.3.1` becomes
`3.4.0`). Only text which lacks digits will fail coercion (`version one`
is not valid). The maximum length for any semver component considered for
coercion is 16 characters; longer components will be ignored
(`10000000000000000.4.7.4` becomes `4.7.4`). The maximum value for any
semver component is `Number.MAX_SAFE_INTEGER || (2**53 - 1)`; higher value
components are invalid (`9999999999999999.4.7.4` is likely invalid).
If the `options.rtl` flag is set, then `coerce` will return the right-most
coercible tuple that does not share an ending index with a longer coercible
tuple. For example, `1.2.3.4` will return `2.3.4` in rtl mode, not
`4.0.0`. `1.2.3/4` will return `4.0.0`, because the `4` is not a part of
any other overlapping SemVer tuple.
If the `options.includePrerelease` flag is set, then the `coerce` result will contain
prerelease and build parts of a version. For example, `1.2.3.4-rc.1+rev.2`
will preserve prerelease `rc.1` and build `rev.2` in the result.
### Clean
* `clean(version)`: Clean a string to be a valid semver if possible
This will return a cleaned and trimmed semver version. If the provided
version is not valid a null will be returned. This does not work for
ranges.
ex.
* `s.clean(' = v 2.1.5foo')`: `null`
* `s.clean(' = v 2.1.5foo', { loose: true })`: `'2.1.5-foo'`
* `s.clean(' = v 2.1.5-foo')`: `null`
* `s.clean(' = v 2.1.5-foo', { loose: true })`: `'2.1.5-foo'`
* `s.clean('=v2.1.5')`: `'2.1.5'`
* `s.clean(' =v2.1.5')`: `'2.1.5'`
* `s.clean(' 2.1.5 ')`: `'2.1.5'`
* `s.clean('~1.0.0')`: `null`
## Constants
As a convenience, helper constants are exported to provide information about what `node-semver` supports:
### `RELEASE_TYPES`
- major
- premajor
- minor
- preminor
- patch
- prepatch
- prerelease
```
const semver = require('semver');
if (semver.RELEASE_TYPES.includes(arbitraryUserInput)) {
console.log('This is a valid release type!');
} else {
console.warn('This is NOT a valid release type!');
}
```
### `SEMVER_SPEC_VERSION`
2.0.0
```
const semver = require('semver');
console.log('We are currently using the semver specification version:', semver.SEMVER_SPEC_VERSION);
```
## Exported Modules
<!--
TODO: Make sure that all of these items are documented (classes aren't,
eg), and then pull the module name into the documentation for that specific
thing.
-->
You may pull in just the part of this semver utility that you need if you
are sensitive to packing and tree-shaking concerns. The main
`require('semver')` export uses getter functions to lazily load the parts
of the API that are used.
The following modules are available:
* `require('semver')`
* `require('semver/classes')`
* `require('semver/classes/comparator')`
* `require('semver/classes/range')`
* `require('semver/classes/semver')`
* `require('semver/functions/clean')`
* `require('semver/functions/cmp')`
* `require('semver/functions/coerce')`
* `require('semver/functions/compare')`
* `require('semver/functions/compare-build')`
* `require('semver/functions/compare-loose')`
* `require('semver/functions/diff')`
* `require('semver/functions/eq')`
* `require('semver/functions/gt')`
* `require('semver/functions/gte')`
* `require('semver/functions/inc')`
* `require('semver/functions/lt')`
* `require('semver/functions/lte')`
* `require('semver/functions/major')`
* `require('semver/functions/minor')`
* `require('semver/functions/neq')`
* `require('semver/functions/parse')`
* `require('semver/functions/patch')`
* `require('semver/functions/prerelease')`
* `require('semver/functions/rcompare')`
* `require('semver/functions/rsort')`
* `require('semver/functions/satisfies')`
* `require('semver/functions/sort')`
* `require('semver/functions/valid')`
* `require('semver/ranges/gtr')`
* `require('semver/ranges/intersects')`
* `require('semver/ranges/ltr')`
* `require('semver/ranges/max-satisfying')`
* `require('semver/ranges/min-satisfying')`
* `require('semver/ranges/min-version')`
* `require('semver/ranges/outside')`
* `require('semver/ranges/simplify')`
* `require('semver/ranges/subset')`
* `require('semver/ranges/to-comparators')`
* `require('semver/ranges/valid')`
+191
View File
@@ -0,0 +1,191 @@
#!/usr/bin/env node
// Standalone semver comparison program.
// Exits successfully and prints matching version(s) if
// any supplied version is valid and passes all tests.
'use strict'
const argv = process.argv.slice(2)
let versions = []
const range = []
let inc = null
const version = require('../package.json').version
let loose = false
let includePrerelease = false
let coerce = false
let rtl = false
let identifier
let identifierBase
const semver = require('../')
const parseOptions = require('../internal/parse-options')
let reverse = false
let options = {}
const main = () => {
if (!argv.length) {
return help()
}
while (argv.length) {
let a = argv.shift()
const indexOfEqualSign = a.indexOf('=')
if (indexOfEqualSign !== -1) {
const value = a.slice(indexOfEqualSign + 1)
a = a.slice(0, indexOfEqualSign)
argv.unshift(value)
}
switch (a) {
case '-rv': case '-rev': case '--rev': case '--reverse':
reverse = true
break
case '-l': case '--loose':
loose = true
break
case '-p': case '--include-prerelease':
includePrerelease = true
break
case '-v': case '--version':
versions.push(argv.shift())
break
case '-i': case '--inc': case '--increment':
switch (argv[0]) {
case 'major': case 'minor': case 'patch': case 'prerelease':
case 'premajor': case 'preminor': case 'prepatch':
case 'release':
inc = argv.shift()
break
default:
inc = 'patch'
break
}
break
case '--preid':
identifier = argv.shift()
break
case '-r': case '--range':
range.push(argv.shift())
break
case '-n':
identifierBase = argv.shift()
if (identifierBase === 'false') {
identifierBase = false
}
break
case '-c': case '--coerce':
coerce = true
break
case '--rtl':
rtl = true
break
case '--ltr':
rtl = false
break
case '-h': case '--help': case '-?':
return help()
default:
versions.push(a)
break
}
}
options = parseOptions({ loose, includePrerelease, rtl })
versions = versions.map((v) => {
return coerce ? (semver.coerce(v, options) || { version: v }).version : v
}).filter((v) => {
return semver.valid(v)
})
if (!versions.length) {
return fail()
}
if (inc && (versions.length !== 1 || range.length)) {
return failInc()
}
for (let i = 0, l = range.length; i < l; i++) {
versions = versions.filter((v) => {
return semver.satisfies(v, range[i], options)
})
if (!versions.length) {
return fail()
}
}
versions
.sort((a, b) => semver[reverse ? 'rcompare' : 'compare'](a, b, options))
.map(v => semver.clean(v, options))
.map(v => inc ? semver.inc(v, inc, options, identifier, identifierBase) : v)
.forEach(v => console.log(v))
}
const failInc = () => {
console.error('--inc can only be used on a single version with no range')
fail()
}
const fail = () => process.exit(1)
const help = () => console.log(
`SemVer ${version}
A JavaScript implementation of the https://semver.org/ specification
Copyright Isaac Z. Schlueter
Usage: semver [options] <version> [<version> [...]]
Prints valid versions sorted by SemVer precedence
Options:
-r --range <range>
Print versions that match the specified range.
-i --increment [<level>]
Increment a version by the specified level. Level can
be one of: major, minor, patch, premajor, preminor,
prepatch, prerelease, or release. Default level is 'patch'.
Only one version may be specified.
--preid <identifier>
Identifier to be used to prefix premajor, preminor,
prepatch or prerelease version increments.
-l --loose
Interpret versions and ranges loosely
-p --include-prerelease
Always include prerelease versions in range matching
-c --coerce
Coerce a string into SemVer if possible
(does not imply --loose)
--rtl
Coerce version strings right to left
--ltr
Coerce version strings left to right (default)
-n <base>
Base number to be used for the prerelease identifier.
Can be either 0 or 1, or false to omit the number altogether.
Defaults to 0.
Program exits successfully if any valid version satisfies
all supplied ranges, and prints all satisfying versions.
If no satisfying versions are found, then exits failure.
Versions are printed in ascending order, so supplying
multiple versions to the utility will just sort them.`)
main()

Some files were not shown because too many files have changed in this diff Show More