feat(smart-app): implement complete mobile app MVP

- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
This commit is contained in:
Eric FELIXINE
2026-06-01 18:00:35 -04:00
parent 08ca495bde
commit e30ae8ed09
35578 changed files with 3703534 additions and 43 deletions

View File

@@ -0,0 +1,88 @@
import type { SpawnOptions, SpawnResult } from '@expo/spawn-async';
import spawnAsync from '@expo/spawn-async';
import type { Ora } from 'ora';
export type CocoaPodsErrorCode = 'NON_INTERACTIVE' | 'NO_CLI' | 'COMMAND_FAILED';
export declare class CocoaPodsError extends Error {
code: CocoaPodsErrorCode;
cause?: Error | undefined;
readonly name = "CocoaPodsError";
readonly isPackageManagerError = true;
constructor(message: string, code: CocoaPodsErrorCode, cause?: Error | undefined);
}
export declare function extractMissingDependencyError(errorOutput: string): [string, string] | null;
export declare class CocoaPodsPackageManager {
#private;
options: SpawnOptions;
private silent;
private cwd;
static getPodProjectRoot(projectRoot: string): string | null;
static isUsingPods(projectRoot: string): boolean;
/** @deprecated: Use `CocoaPodsPackageManager#installCLIAsync` instead */
static gemInstallCLIAsync(nonInteractive?: boolean, spawnOptions?: SpawnOptions): Promise<void>;
/** @deprecated: Use `CocoaPodsPackageManager#installCLIAsync` instead */
static brewLinkCLIAsync(spawnOptions?: SpawnOptions): Promise<void>;
/** @deprecated: Use `CocoaPodsPackageManager#installCLIAsync` instead */
static brewInstallCLIAsync(spawnOptions?: SpawnOptions): Promise<void>;
/** @deprecated: Use `CocoaPodsPackageManager#installCLIAsync` instead */
static installCLIAsync({ nonInteractive, spawnOptions, }: {
nonInteractive?: boolean;
spawnOptions?: SpawnOptions;
}): Promise<boolean>;
static isAvailable(projectRoot: string, silent: boolean): boolean;
/** @deprecated: Use `CocoaPodsPackageManager#isCLIInstalledAsync` instead */
static isCLIInstalledAsync(spawnOptions?: SpawnOptions, { useBundler }?: {
useBundler?: boolean;
}): Promise<boolean>;
constructor({ cwd, silent, useBundler, }: {
cwd: string;
silent?: boolean;
useBundler?: boolean;
});
private _useBundler?;
get name(): string;
/** Runs `pod install` and attempts to automatically run known troubleshooting steps automatically. */
installAsync({ spinner }?: {
spinner?: Ora;
}): Promise<void>;
isCLIInstalledAsync(): Promise<boolean>;
installCLIAsync({ nonInteractive, spawnOptions, }?: {
nonInteractive?: boolean;
spawnOptions?: spawnAsync.SpawnOptions;
}): Promise<boolean>;
handleInstallErrorAsync({ error, shouldUpdate, updatedPackages, spinner, }: {
error: any;
spinner?: Ora;
shouldUpdate?: boolean;
updatedPackages?: string[];
}): Promise<SpawnResult>;
private _installAsync;
private runInstallTypeCommandAsync;
addWithParametersAsync(names: string[], parameters: string[]): Promise<void>;
addAsync(names?: string[]): void;
addDevAsync(names?: string[]): void;
addGlobalAsync(names?: string[]): void;
removeAsync(names?: string[]): void;
removeDevAsync(names?: string[]): void;
removeGlobalAsync(names?: string[]): void;
versionAsync(): Promise<string>;
configAsync(key: string): Promise<string>;
removeLockfileAsync(): Promise<void>;
uninstallAsync(): Promise<void>;
_runAsync(args: string[]): Promise<SpawnResult>;
}
export declare function getPodUpdateMessage(output: string): {
updatePackage: string | null;
shouldUpdateRepo: boolean;
};
export declare function getPodRepoUpdateMessage(errorOutput: string): {
updatePackage: string | null;
shouldUpdateRepo: boolean;
message: string;
};
/**
* Format the CocoaPods CLI install error.
*
* @param error Error from CocoaPods CLI `pod install` command.
* @returns
*/
export declare function getImprovedPodInstallError(error: SpawnResult & Error, { cwd }: Pick<SpawnOptions, 'cwd'>): Error;

View File

@@ -0,0 +1,428 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CocoaPodsPackageManager = exports.CocoaPodsError = void 0;
exports.extractMissingDependencyError = extractMissingDependencyError;
exports.getPodUpdateMessage = getPodUpdateMessage;
exports.getPodRepoUpdateMessage = getPodRepoUpdateMessage;
exports.getImprovedPodInstallError = getImprovedPodInstallError;
const spawn_async_1 = __importDefault(require("@expo/spawn-async"));
const chalk_1 = __importDefault(require("chalk"));
const fs_1 = __importDefault(require("fs"));
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
const gemfile_1 = require("./gemfile");
class CocoaPodsError extends Error {
code;
cause;
name = 'CocoaPodsError';
isPackageManagerError = true;
constructor(message, code, cause) {
super(cause ? `${message}\n└─ Cause: ${cause.message}` : message);
this.code = code;
this.cause = cause;
}
}
exports.CocoaPodsError = CocoaPodsError;
function extractMissingDependencyError(errorOutput) {
// [!] Unable to find a specification for `expo-dev-menu-interface` depended upon by `expo-dev-launcher`
const results = errorOutput.match(/Unable to find a specification for ['"`]([\w-_\d\s]+)['"`] depended upon by ['"`]([\w-_\d\s]+)['"`]/);
if (results != null && results[1] != null && results[2] != null) {
return [results[1], results[2]];
}
return null;
}
class CocoaPodsPackageManager {
options;
silent;
cwd;
static getPodProjectRoot(projectRoot) {
if (CocoaPodsPackageManager.isUsingPods(projectRoot))
return projectRoot;
const iosProject = path_1.default.join(projectRoot, 'ios');
if (CocoaPodsPackageManager.isUsingPods(iosProject))
return iosProject;
const macOsProject = path_1.default.join(projectRoot, 'macos');
if (CocoaPodsPackageManager.isUsingPods(macOsProject))
return macOsProject;
return null;
}
static isUsingPods(projectRoot) {
return fs_1.default.existsSync(path_1.default.join(projectRoot, 'Podfile'));
}
/** @deprecated: Use `CocoaPodsPackageManager#installCLIAsync` instead */
static async gemInstallCLIAsync(nonInteractive = false, spawnOptions = { stdio: 'inherit' }) {
const options = ['install', 'cocoapods', '--no-document'];
try {
// In case the user has run sudo before running the command we can properly install CocoaPods without prompting for an interaction.
await (0, spawn_async_1.default)('gem', options, spawnOptions);
}
catch (error) {
if (nonInteractive) {
throw new CocoaPodsError('Failed to install CocoaPods CLI with gem (recommended)', 'COMMAND_FAILED', error);
}
// If the user doesn't have permission then we can prompt them to use sudo.
console.log('Your password might be needed to install CocoaPods CLI: https://guides.cocoapods.org/using/getting-started.html#installation');
await (0, spawn_async_1.default)('sudo', ['gem', ...options], spawnOptions);
}
}
/** @deprecated: Use `CocoaPodsPackageManager#installCLIAsync` instead */
static async brewLinkCLIAsync(spawnOptions = { stdio: 'inherit' }) {
await (0, spawn_async_1.default)('brew', ['link', 'cocoapods'], spawnOptions);
}
/** @deprecated: Use `CocoaPodsPackageManager#installCLIAsync` instead */
static async brewInstallCLIAsync(spawnOptions = { stdio: 'inherit' }) {
await (0, spawn_async_1.default)('brew', ['install', 'cocoapods'], spawnOptions);
}
/** @deprecated: Use `CocoaPodsPackageManager#installCLIAsync` instead */
static async installCLIAsync({ nonInteractive = false, spawnOptions = { stdio: 'inherit' }, }) {
if (!spawnOptions) {
spawnOptions = { stdio: 'inherit' };
}
const silent = !!spawnOptions.ignoreStdio;
try {
!silent && console.log(`\u203A Attempting to install CocoaPods CLI with Gem`);
await CocoaPodsPackageManager.gemInstallCLIAsync(nonInteractive, spawnOptions);
!silent && console.log(`\u203A Successfully installed CocoaPods CLI with Gem`);
return true;
}
catch (error) {
if (!silent) {
console.log(chalk_1.default.yellow(`\u203A Failed to install CocoaPods CLI with Gem`));
console.log(chalk_1.default.red(error.stderr ?? error.message));
console.log(`\u203A Attempting to install CocoaPods CLI with Homebrew`);
}
try {
await CocoaPodsPackageManager.brewInstallCLIAsync(spawnOptions);
if (!(await CocoaPodsPackageManager.isCLIInstalledAsync(spawnOptions))) {
try {
await CocoaPodsPackageManager.brewLinkCLIAsync(spawnOptions);
// Still not available after linking? Bail out
if (!(await CocoaPodsPackageManager.isCLIInstalledAsync(spawnOptions))) {
throw new CocoaPodsError('CLI could not be installed automatically with gem or Homebrew, please install CocoaPods manually and try again', 'NO_CLI', error);
}
}
catch (error) {
throw new CocoaPodsError('Homebrew installation appeared to succeed but CocoaPods CLI not found in PATH and unable to link.', 'NO_CLI', error);
}
}
!silent && console.log(`\u203A Successfully installed CocoaPods CLI with Homebrew`);
return true;
}
catch (error) {
!silent &&
console.warn(chalk_1.default.yellow(`\u203A Failed to install CocoaPods with Homebrew. Install CocoaPods CLI and try again: https://cocoapods.org/`));
throw new CocoaPodsError(`Failed to install CocoaPods with Homebrew. Install CocoaPods CLI and try again: https://cocoapods.org/`, 'NO_CLI', error);
}
}
}
static isAvailable(projectRoot, silent) {
if (process.platform !== 'darwin') {
!silent && console.log(chalk_1.default.red('CocoaPods is only supported on macOS machines'));
return false;
}
if (!CocoaPodsPackageManager.isUsingPods(projectRoot)) {
!silent && console.log(chalk_1.default.yellow('CocoaPods is not supported in this project'));
return false;
}
return true;
}
/** @deprecated: Use `CocoaPodsPackageManager#isCLIInstalledAsync` instead */
static async isCLIInstalledAsync(spawnOptions = { stdio: 'inherit' }, { useBundler = false } = {}) {
try {
if (useBundler) {
await (0, spawn_async_1.default)('bundle', ['exec', 'pod', '--version'], spawnOptions);
}
else {
await (0, spawn_async_1.default)('pod', ['--version'], spawnOptions);
}
return true;
}
catch (error) {
// NOTE(@kitten): We abort here to prevent any command from proceeding
// We don't want to trigger `installCLIAsync` and let users resolve this
if (useBundler) {
console.warn(chalk_1.default.yellow(`\u203A Failed to run \`bundle exec pod\``));
throw new CocoaPodsError(error.stderr, 'COMMAND_FAILED');
}
return false;
}
}
constructor({ cwd, silent, useBundler, }) {
this.silent = !!silent;
this.cwd = cwd;
this.options = {
cwd,
// We use pipe by default instead of inherit so that we can capture stderr/stdout and process it for errors.
// Later we'll also pipe the stdout/stderr to the terminal when silent is false.
stdio: 'pipe',
};
// NOTE(@kitten): Override for tests
if (useBundler != null) {
this._useBundler = Promise.resolve(useBundler);
}
}
_useBundler;
async #useBundlerAsync() {
return (this._useBundler ||= (0, gemfile_1.isUsingBundlerAsync)(this.cwd));
}
get name() {
return 'CocoaPods';
}
/** Runs `pod install` and attempts to automatically run known troubleshooting steps automatically. */
async installAsync({ spinner } = {}) {
await this._installAsync({ spinner });
}
async isCLIInstalledAsync() {
return CocoaPodsPackageManager.isCLIInstalledAsync(this.options, {
useBundler: await this.#useBundlerAsync(),
});
}
installCLIAsync({ nonInteractive = true, spawnOptions, } = {}) {
return CocoaPodsPackageManager.installCLIAsync({
nonInteractive,
spawnOptions: {
...this.options,
...spawnOptions,
},
});
}
async handleInstallErrorAsync({ error, shouldUpdate = true, updatedPackages = [], spinner, }) {
// Unknown errors are rethrown.
if (!error.output) {
throw error;
}
// To emulate a `pod install --repo-update` error, enter your `ios/Podfile.lock` and change one of `PODS` version numbers to some lower value.
// const isPodRepoUpdateError = shouldPodRepoUpdate(output);
if (!shouldUpdate) {
// If we can't automatically fix the error, we'll just rethrow it with some known troubleshooting info.
throw getImprovedPodInstallError(error, {
cwd: this.options.cwd,
});
}
// Collect all of the spawn info.
const errorOutput = error.output.join(os_1.default.EOL).trim();
// Extract useful information from the error message and push it to the spinner.
const { updatePackage, shouldUpdateRepo } = getPodUpdateMessage(errorOutput);
if (!updatePackage || updatedPackages.includes(updatePackage)) {
// `pod install --repo-update`...
// Attempt to install again but this time with install --repo-update enabled.
return await this._installAsync({
spinner,
shouldRepoUpdate: true,
// Include a boolean to ensure pod install --repo-update isn't invoked in the unlikely case where the pods fail to update.
shouldUpdate: false,
updatedPackages,
});
}
// Store the package we should update to prevent a loop.
updatedPackages.push(updatePackage);
// If a single package is broken, we'll try to update it.
// You can manually test this by changing a version number in your `Podfile.lock`.
// Attempt `pod update <package> <--no-repo-update>` and then try again.
return await this.runInstallTypeCommandAsync(['update', updatePackage, shouldUpdateRepo ? '' : '--no-repo-update'].filter(Boolean), {
formatWarning() {
const updateMessage = `Failed to update ${chalk_1.default.bold(updatePackage)}. Attempting to update the repo instead.`;
return updateMessage;
},
spinner,
updatedPackages,
});
// // If update succeeds, we'll try to install again (skipping `pod install --repo-update`).
// return await this._installAsync({
// spinner,
// shouldUpdate: false,
// updatedPackages,
// });
}
async _installAsync({ shouldRepoUpdate, ...props } = {}) {
return await this.runInstallTypeCommandAsync(['install', shouldRepoUpdate ? '--repo-update' : ''].filter(Boolean), {
formatWarning(error) {
// Extract useful information from the error message and push it to the spinner.
return getPodRepoUpdateMessage(error.output.join(os_1.default.EOL).trim()).message;
},
...props,
});
}
async runInstallTypeCommandAsync(command, { formatWarning, ...props } = {}) {
try {
return await this._runAsync(command);
}
catch (error) {
if (formatWarning) {
const warning = formatWarning(error);
if (props.spinner) {
props.spinner.text = chalk_1.default.bold(warning);
}
if (!this.silent) {
console.warn(chalk_1.default.yellow(warning));
}
}
return await this.handleInstallErrorAsync({ error, ...props });
}
}
async addWithParametersAsync(names, parameters) {
throw new Error('Unimplemented');
}
addAsync(names = []) {
throw new Error('Unimplemented');
}
addDevAsync(names = []) {
throw new Error('Unimplemented');
}
addGlobalAsync(names = []) {
throw new Error('Unimplemented');
}
removeAsync(names = []) {
throw new Error('Unimplemented');
}
removeDevAsync(names = []) {
throw new Error('Unimplemented');
}
removeGlobalAsync(names = []) {
throw new Error('Unimplemented');
}
async versionAsync() {
const { stdout } = (await this.#useBundlerAsync())
? await (0, spawn_async_1.default)('bundle', ['exec', 'pod', '--version'], this.options)
: await (0, spawn_async_1.default)('pod', ['--version'], this.options);
return stdout.trim();
}
async configAsync(key) {
throw new Error('Unimplemented');
}
async removeLockfileAsync() {
throw new Error('Unimplemented');
}
async uninstallAsync() {
throw new Error('Unimplemented');
}
// Exposed for testing
async _runAsync(args) {
const useBundler = await this.#useBundlerAsync();
const [command, commandArgs] = useBundler
? ['bundle', ['exec', 'pod', ...args, '--ansi']]
: ['pod', [...args, '--ansi']];
if (!this.silent) {
console.log(`> ${useBundler ? 'bundle exec pod' : 'pod'} ${args.join(' ')}`);
}
const promise = (0, spawn_async_1.default)(command, commandArgs, {
// Add the cwd and other options to the spawn options.
...this.options,
// We use pipe by default instead of inherit so that we can capture stderr/stdout and process it for errors.
// This is particularly required for the `pod install --repo-update` error.
// Later we'll also pipe the stdout/stderr to the terminal when silent is false,
// currently this means we lose out on the ansi colors unless passing the `--ansi` flag to every command.
stdio: 'pipe',
});
if (!this.silent) {
// If not silent, pipe the stdout/stderr to the terminal.
// We only do this when the `stdio` is set to `pipe` (collect the results for parsing), `inherit` won't contain `promise.child`.
if (promise.child.stdout) {
promise.child.stdout.pipe(process.stdout);
}
}
return await promise;
}
}
exports.CocoaPodsPackageManager = CocoaPodsPackageManager;
/** When pods are outdated, they'll throw an error informing you to run "pod install --repo-update" */
function shouldPodRepoUpdate(errorOutput) {
const output = errorOutput;
const isPodRepoUpdateError = output.includes('pod repo update') || output.includes('--no-repo-update');
return isPodRepoUpdateError;
}
function getPodUpdateMessage(output) {
const props = output.match(/run ['"`]pod update ([\w-_\d/]+)( --no-repo-update)?['"`] to apply changes/);
return {
updatePackage: props?.[1] ?? null,
shouldUpdateRepo: !props?.[2],
};
}
function getPodRepoUpdateMessage(errorOutput) {
const warningInfo = extractMissingDependencyError(errorOutput);
const brokenPackage = getPodUpdateMessage(errorOutput);
let message;
if (warningInfo) {
message = `Couldn't install: ${warningInfo[1]} » ${chalk_1.default.underline(warningInfo[0])}.`;
}
else if (brokenPackage?.updatePackage) {
message = `Couldn't install: ${brokenPackage?.updatePackage}.`;
}
else {
message = `Couldn't install Pods.`;
}
message += ` Updating the Pods project and trying again...`;
return { message, ...brokenPackage };
}
/**
* Format the CocoaPods CLI install error.
*
* @param error Error from CocoaPods CLI `pod install` command.
* @returns
*/
function getImprovedPodInstallError(error, { cwd = process.cwd() }) {
// Collect all of the spawn info.
const errorOutput = error.output.join(os_1.default.EOL).trim();
if (error.stdout.match(/No [`'"]Podfile[`'"] found in the project directory/)) {
// Ran pod install but no Podfile was found.
error.message = `No Podfile found in directory: ${cwd}. Ensure CocoaPods is setup any try again.`;
}
else if (shouldPodRepoUpdate(errorOutput)) {
// Ran pod install but the install --repo-update step failed.
const warningInfo = extractMissingDependencyError(errorOutput);
let reason;
if (warningInfo) {
reason = `Couldn't install: ${warningInfo[1]} » ${chalk_1.default.underline(warningInfo[0])}`;
}
else {
reason = `This is often due to native package versions mismatching`;
}
// Attempt to provide a helpful message about the missing NPM dependency (containing a CocoaPod) since React Native
// developers will almost always be using autolinking and not interacting with CocoaPods directly.
let solution;
if (warningInfo?.[0]) {
// If the missing package is named `expo-dev-menu`, `react-native`, etc. then it might not be installed in the project.
if (warningInfo[0].match(/^(?:@?expo|@?react)(-|\/)/)) {
solution = `Ensure the node module "${warningInfo[0]}" is installed in your project, then run 'npx pod-install' to try again.`;
}
else {
solution = `Ensure the CocoaPod "${warningInfo[0]}" is installed in your project, then run 'npx pod-install' to try again.`;
}
}
else {
// Brute force
solution = `Try deleting the 'ios/Pods' folder or the 'ios/Podfile.lock' file and running 'npx pod-install' to resolve.`;
}
error.message = `${reason}. ${solution}`;
// Attempt to provide the troubleshooting info from CocoaPods CLI at the bottom of the error message.
if (error.stdout) {
const cocoapodsDebugInfo = error.stdout.split(os_1.default.EOL);
// The troubleshooting info starts with `[!]`, capture everything after that.
const firstWarning = cocoapodsDebugInfo.findIndex((v) => v.startsWith('[!]'));
if (firstWarning !== -1) {
const warning = cocoapodsDebugInfo.slice(firstWarning).join(os_1.default.EOL);
error.message += `\n\n${chalk_1.default.gray(warning)}`;
}
}
return new CocoaPodsError('Command `pod install --repo-update` failed.', 'COMMAND_FAILED', error);
}
else {
let stderr = error.stderr.trim();
// CocoaPods CLI prints the useful error to stdout...
const usefulError = error.stdout.match(/\[!\]\s((?:.|\n)*)/)?.[1];
// If there is a useful error message then prune the less useful info.
if (usefulError) {
// Delete unhelpful CocoaPods CLI error message.
if (error.message?.match(/pod exited with non-zero code: 1/)) {
error.message = '';
}
stderr = null;
}
error.message = [usefulError, error.message, stderr].filter(Boolean).join('\n');
}
return new CocoaPodsError('Command `pod install` failed.', 'COMMAND_FAILED', error);
}
//# sourceMappingURL=CocoaPodsPackageManager.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/** Finds a Gemfile in the target directory or a parent, stopping at the Git or workspace root. */
export declare function findGemfile(root?: string): Promise<string | null>;
/**
* Check if the project uses Bundler to manage CocoaPods.
* Returns `true` if a `Gemfile` exists in the project root (or a parent)
* that lists `cocoapods` as a dependency, and `bundle exec pod --version` succeeds.
*/
export declare function isUsingBundlerAsync(projectRoot: string): Promise<boolean>;

View File

@@ -0,0 +1,79 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.findGemfile = findGemfile;
exports.isUsingBundlerAsync = isUsingBundlerAsync;
const spawn_async_1 = __importDefault(require("@expo/spawn-async"));
const fs_1 = __importDefault(require("fs"));
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
const resolve_workspace_root_1 = require("resolve-workspace-root");
const maybeStat = (targetPath) => fs_1.default.promises.stat(path_1.default.resolve(targetPath)).catch(() => null);
async function getGitTopLevelAsync(root = process.cwd()) {
try {
const output = await (0, spawn_async_1.default)('git', ['rev-parse', '--show-toplevel'], { cwd: root });
const gitRoot = path_1.default.resolve(output.stdout.trim());
return (await maybeStat(gitRoot))?.isDirectory() ? gitRoot : null;
}
catch {
return null;
}
}
async function getGitRootPathAsync(root = process.cwd()) {
const homeDir = path_1.default.normalize(os_1.default.homedir());
for (let dir = root; path_1.default.dirname(dir) !== dir; dir = path_1.default.dirname(dir)) {
if ((await maybeStat(path_1.default.join(dir, '.git')))?.isDirectory()) {
return dir;
}
if (dir === homeDir)
break;
}
return null;
}
async function getRootPathAsync(root = process.cwd()) {
const rootPath = (await getGitTopLevelAsync(root)) ||
(await getGitRootPathAsync(root)) ||
(await (0, resolve_workspace_root_1.resolveWorkspaceRootAsync)(root)) ||
process.cwd();
return rootPath;
}
/** Finds a Gemfile in the target directory or a parent, stopping at the Git or workspace root. */
async function findGemfile(root = process.cwd()) {
const rootBoundary = path_1.default.normalize(await getRootPathAsync(root));
for (let dir = root; path_1.default.dirname(dir) !== dir; dir = path_1.default.dirname(dir)) {
const candidate = path_1.default.join(dir, 'Gemfile');
if ((await maybeStat(candidate))?.isFile()) {
return candidate;
}
if (dir === rootBoundary)
break;
}
return null;
}
/**
* Check if the project uses Bundler to manage CocoaPods.
* Returns `true` if a `Gemfile` exists in the project root (or a parent)
* that lists `cocoapods` as a dependency, and `bundle exec pod --version` succeeds.
*/
async function isUsingBundlerAsync(projectRoot) {
const gemfilePath = await findGemfile(projectRoot);
if (!gemfilePath) {
return false;
}
const [gemfileContents, podExec] = await Promise.allSettled([
fs_1.default.promises.readFile(gemfilePath, 'utf8'),
(0, spawn_async_1.default)('bundle', ['exec', 'pod', '--version'], {
cwd: projectRoot,
stdio: 'ignore',
}),
]);
if (gemfileContents.status === 'rejected' || podExec.status === 'rejected') {
return false;
}
else {
return /gem\s*\(?\s*(?:'cocoapods'|"cocoapods")/.test(gemfileContents.value);
}
}
//# sourceMappingURL=gemfile.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"gemfile.js","sourceRoot":"","sources":["../../src/ios/gemfile.ts"],"names":[],"mappings":"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"}