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,19 @@
import type { RNConfigDependencyAndroid, RNConfigReactNativePlatformsConfigAndroid } from './reactNativeConfig.types';
export declare function resolveDependencyConfigImplAndroidAsync(packageRoot: string, reactNativeConfig: RNConfigReactNativePlatformsConfigAndroid | null | undefined): Promise<RNConfigDependencyAndroid | null>;
/**
* Parse the `RNConfigDependencyAndroid.packageName`
*/
export declare function parsePackageNameAsync(manifestPath: string | null, gradlePath: string | null): Promise<string | null>;
/**
* Parse the Java or Kotlin class name to for `ReactPackage` or `TurboReactPackage`.
*/
export declare function parseNativePackageClassNameAsync(packageRoot: string, androidDir: string): Promise<string | null>;
export declare function parseLibraryNameAsync(androidDir: string, packageJson: any): Promise<string | null>;
export declare function parseComponentDescriptorsAsync(packageRoot: string, pacakgeJson: any): Promise<string[]>;
export declare function findGradleAndManifestAsync({ androidDir, isLibrary, }: {
androidDir: string;
isLibrary: boolean;
}): Promise<{
gradle: string;
manifest: string;
}>;

View File

@@ -0,0 +1,208 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.findGradleAndManifestAsync = exports.parseComponentDescriptorsAsync = exports.parseLibraryNameAsync = exports.parseNativePackageClassNameAsync = exports.parsePackageNameAsync = exports.resolveDependencyConfigImplAndroidAsync = void 0;
const fast_glob_1 = __importDefault(require("fast-glob"));
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
const utils_1 = require("./utils");
async function resolveDependencyConfigImplAndroidAsync(packageRoot, reactNativeConfig) {
if (reactNativeConfig === null) {
// Skip autolinking for this package.
return null;
}
const androidDir = path_1.default.join(packageRoot, 'android');
const { gradle, manifest } = await findGradleAndManifestAsync({ androidDir, isLibrary: true });
if (!manifest && !gradle) {
return null;
}
const packageName = reactNativeConfig?.packageName ||
(await parsePackageNameAsync(path_1.default.join(androidDir, manifest), path_1.default.join(androidDir, gradle)));
const nativePackageClassName = await parseNativePackageClassNameAsync(packageRoot, androidDir);
if (!nativePackageClassName) {
return null;
}
const packageJson = JSON.parse(await promises_1.default.readFile(path_1.default.join(packageRoot, 'package.json'), 'utf8'));
const packageImportPath = reactNativeConfig?.packageImportPath || `import ${packageName}.${nativePackageClassName};`;
const packageInstance = reactNativeConfig?.packageInstance || `new ${nativePackageClassName}()`;
const buildTypes = reactNativeConfig?.buildTypes || [];
const dependencyConfiguration = reactNativeConfig?.dependencyConfiguration;
const libraryName = reactNativeConfig?.libraryName || (await parseLibraryNameAsync(androidDir, packageJson));
const componentDescriptors = reactNativeConfig?.componentDescriptors ||
(await parseComponentDescriptorsAsync(packageRoot, packageJson));
let cmakeListsPath = reactNativeConfig?.cmakeListsPath
? path_1.default.join(androidDir, reactNativeConfig?.cmakeListsPath)
: path_1.default.join(androidDir, 'build/generated/source/codegen/jni/CMakeLists.txt');
const cxxModuleCMakeListsModuleName = reactNativeConfig?.cxxModuleCMakeListsModuleName || null;
const cxxModuleHeaderName = reactNativeConfig?.cxxModuleHeaderName || null;
let cxxModuleCMakeListsPath = reactNativeConfig?.cxxModuleCMakeListsPath
? path_1.default.join(androidDir, reactNativeConfig?.cxxModuleCMakeListsPath)
: null;
if (process.platform === 'win32') {
cmakeListsPath = cmakeListsPath.replace(/\\/g, '/');
if (cxxModuleCMakeListsPath) {
cxxModuleCMakeListsPath = cxxModuleCMakeListsPath.replace(/\\/g, '/');
}
}
const result = {
sourceDir: androidDir,
packageImportPath,
packageInstance,
dependencyConfiguration,
buildTypes,
libraryName,
componentDescriptors,
cmakeListsPath,
cxxModuleCMakeListsModuleName,
cxxModuleCMakeListsPath,
cxxModuleHeaderName,
};
if (!result.libraryName) {
delete result.libraryName;
}
if (!result.dependencyConfiguration) {
delete result.dependencyConfiguration;
}
return result;
}
exports.resolveDependencyConfigImplAndroidAsync = resolveDependencyConfigImplAndroidAsync;
/**
* Parse the `RNConfigDependencyAndroid.packageName`
*/
async function parsePackageNameAsync(manifestPath, gradlePath) {
if (gradlePath) {
const gradleContents = await promises_1.default.readFile(gradlePath, 'utf8');
const match = gradleContents.match(/namespace\s*[=]*\s*["'](.+?)["']/);
if (match) {
return match[1];
}
}
if (manifestPath) {
const manifestContents = await promises_1.default.readFile(manifestPath, 'utf8');
const match = manifestContents.match(/package="(.+?)"/);
if (match) {
return match[1];
}
}
return null;
}
exports.parsePackageNameAsync = parsePackageNameAsync;
/**
* Parse the Java or Kotlin class name to for `ReactPackage` or `TurboReactPackage`.
*/
async function parseNativePackageClassNameAsync(packageRoot, androidDir) {
const matched = await (0, utils_1.globMatchFunctorFirstAsync)('**/*Package.{java,kt}', matchNativePackageClassName, { cwd: androidDir });
if (matched) {
return matched;
}
// Early return if the module is an Expo module
if (await (0, utils_1.fileExistsAsync)(path_1.default.join(packageRoot, 'expo-module.config.json'))) {
return null;
}
return await (0, utils_1.globMatchFunctorFirstAsync)('**/*.{java,kt}', matchNativePackageClassName, {
cwd: androidDir,
});
}
exports.parseNativePackageClassNameAsync = parseNativePackageClassNameAsync;
let lazyReactPackageRegex = null;
let lazyTurboReactPackageRegex = null;
function matchNativePackageClassName(filePath, contents) {
const fileContents = contents.toString();
// [0] Match ReactPackage
if (!lazyReactPackageRegex) {
lazyReactPackageRegex =
/class\s+(\w+[^(\s]*)[\s\w():]*(\s+implements\s+|:)[\s\w():,]*[^{]*ReactPackage/;
}
const matchReactPackage = fileContents.match(lazyReactPackageRegex);
if (matchReactPackage) {
return matchReactPackage[1];
}
// [1] Match TurboReactPackage
if (!lazyTurboReactPackageRegex) {
lazyTurboReactPackageRegex =
/class\s+(\w+[^(\s]*)[\s\w():]*(\s+extends\s+|:)[\s\w():,]*[^{]*TurboReactPackage/;
}
const matchTurboReactPackage = fileContents.match(lazyTurboReactPackageRegex);
if (matchTurboReactPackage) {
return matchTurboReactPackage[1];
}
return null;
}
async function parseLibraryNameAsync(androidDir, packageJson) {
// [0] `codegenConfig.name` from package.json
if (packageJson.codegenConfig?.name) {
return packageJson.codegenConfig.name;
}
const libraryNameRegExp = /libraryName = ["'](.+)["']/;
const gradlePath = path_1.default.join(androidDir, 'build.gradle');
// [1] `libraryName` from build.gradle
if (await (0, utils_1.fileExistsAsync)(gradlePath)) {
const buildGradleContents = await promises_1.default.readFile(gradlePath, 'utf8');
const match = buildGradleContents.match(libraryNameRegExp);
if (match) {
return match[1];
}
}
// [2] `libraryName` from build.gradle.kts
const gradleKtsPath = path_1.default.join(androidDir, 'build.gradle.kts');
if (await (0, utils_1.fileExistsAsync)(gradleKtsPath)) {
const buildGradleContents = await promises_1.default.readFile(gradleKtsPath, 'utf8');
const match = buildGradleContents.match(libraryNameRegExp);
if (match) {
return match[1];
}
}
return null;
}
exports.parseLibraryNameAsync = parseLibraryNameAsync;
async function parseComponentDescriptorsAsync(packageRoot, pacakgeJson) {
const jsRoot = pacakgeJson?.codegenConfig?.jsSrcsDir
? path_1.default.join(packageRoot, pacakgeJson.codegenConfig.jsSrcsDir)
: packageRoot;
const results = await (0, utils_1.globMatchFunctorAllAsync)('**/*.{js,jsx,ts,tsx}', matchComponentDescriptors, {
cwd: jsRoot,
ignore: ['**/node_modules/**'],
});
// Filter out duplicates as it happens that libraries contain multiple outputs due to package publishing.
// TODO: consider using "codegenConfig" to avoid this.
return Array.from(new Set(results));
}
exports.parseComponentDescriptorsAsync = parseComponentDescriptorsAsync;
let lazyCodegenComponentRegex = null;
function matchComponentDescriptors(filePath, contents) {
const fileContents = contents.toString();
if (!lazyCodegenComponentRegex) {
lazyCodegenComponentRegex =
/codegenNativeComponent(<.*>)?\s*\(\s*["'`](\w+)["'`](,?[\s\S]+interfaceOnly:\s*(\w+))?/m;
}
const match = fileContents.match(lazyCodegenComponentRegex);
if (!(match?.[4] === 'true') && match?.[2]) {
return `${match[2]}ComponentDescriptor`;
}
return null;
}
async function findGradleAndManifestAsync({ androidDir, isLibrary, }) {
const globExcludes = [
'node_modules/**',
'**/build/**',
'**/debug/**',
'Examples/**',
'examples/**',
'**/Pods/**',
'**/sdks/hermes/android/**',
'**/src/androidTest/**',
'**/src/test/**',
];
const gradlePattern = isLibrary ? 'build.gradle{,.kts}' : 'app/build.gradle{,.kts}';
const [manifests, gradles] = await Promise.all([
(0, fast_glob_1.default)('**/AndroidManifest.xml', { cwd: androidDir, ignore: globExcludes }),
(0, fast_glob_1.default)(gradlePattern, { cwd: androidDir, ignore: globExcludes }),
]);
const manifest = manifests.find((manifest) => manifest.includes('src/main/')) ?? manifests[0];
const gradle = gradles[0];
return { gradle, manifest };
}
exports.findGradleAndManifestAsync = findGradleAndManifestAsync;
//# sourceMappingURL=androidResolver.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,5 @@
import type { RNConfigReactNativeConfig } from './reactNativeConfig.types';
/**
* Load the `react-native.config.js` or `react-native.config.ts` from the package.
*/
export declare function loadConfigAsync<T extends RNConfigReactNativeConfig>(packageRoot: string): Promise<T | null>;

View File

@@ -0,0 +1,56 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadConfigAsync = void 0;
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
const require_from_string_1 = __importDefault(require("require-from-string"));
const resolve_from_1 = __importDefault(require("resolve-from"));
const utils_1 = require("./utils");
let tsMain = undefined;
/**
* Load the `react-native.config.js` or `react-native.config.ts` from the package.
*/
async function loadConfigAsync(packageRoot) {
const configJsPath = path_1.default.join(packageRoot, 'react-native.config.js');
if (await (0, utils_1.fileExistsAsync)(configJsPath)) {
try {
return require(configJsPath);
}
catch {
return null;
}
}
const configTsPath = path_1.default.join(packageRoot, 'react-native.config.ts');
if (await (0, utils_1.fileExistsAsync)(configTsPath)) {
if (tsMain === undefined) {
const tsPath = resolve_from_1.default.silent(packageRoot, 'typescript');
if (tsPath) {
tsMain = require(tsPath);
}
}
else if (tsMain == null) {
return null;
}
const configContents = await promises_1.default.readFile(configTsPath, 'utf8');
const transpiledContents = tsMain?.transpileModule(configContents, {
compilerOptions: {
module: tsMain.ModuleKind.NodeNext,
moduleResolution: tsMain.ModuleResolutionKind.NodeNext,
target: tsMain.ScriptTarget.ESNext,
},
});
const outputText = transpiledContents?.outputText;
let config;
try {
config = outputText ? (0, require_from_string_1.default)(outputText) : null;
}
catch { }
return config?.default ?? config ?? null;
}
return null;
}
exports.loadConfigAsync = loadConfigAsync;
//# sourceMappingURL=config.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/reactNativeConfig/config.ts"],"names":[],"mappings":";;;;;;AAAA,2DAA6B;AAC7B,gDAAwB;AACxB,8EAAoD;AACpD,gEAAuC;AAGvC,mCAA0C;AAE1C,IAAI,MAAM,GAAmD,SAAS,CAAC;AAEvE;;GAEG;AACI,KAAK,UAAU,eAAe,CACnC,WAAmB;IAEnB,MAAM,YAAY,GAAG,cAAI,CAAC,IAAI,CAAC,WAAW,EAAE,wBAAwB,CAAC,CAAC;IACtE,IAAI,MAAM,IAAA,uBAAe,EAAC,YAAY,CAAC,EAAE;QACvC,IAAI;YACF,OAAO,OAAO,CAAC,YAAY,CAAC,CAAC;SAC9B;QAAC,MAAM;YACN,OAAO,IAAI,CAAC;SACb;KACF;IAED,MAAM,YAAY,GAAG,cAAI,CAAC,IAAI,CAAC,WAAW,EAAE,wBAAwB,CAAC,CAAC;IACtE,IAAI,MAAM,IAAA,uBAAe,EAAC,YAAY,CAAC,EAAE;QACvC,IAAI,MAAM,KAAK,SAAS,EAAE;YACxB,MAAM,MAAM,GAAG,sBAAW,CAAC,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;YAC7D,IAAI,MAAM,EAAE;gBACV,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;aAC1B;SACF;aAAM,IAAI,MAAM,IAAI,IAAI,EAAE;YACzB,OAAO,IAAI,CAAC;SACb;QAED,MAAM,cAAc,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QAC/D,MAAM,kBAAkB,GAAG,MAAM,EAAE,eAAe,CAAC,cAAc,EAAE;YACjE,eAAe,EAAE;gBACf,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,QAAQ;gBAClC,gBAAgB,EAAE,MAAM,CAAC,oBAAoB,CAAC,QAAQ;gBACtD,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC,MAAM;aACnC;SACF,CAAC,CAAC;QACH,MAAM,UAAU,GAAG,kBAAkB,EAAE,UAAU,CAAC;QAClD,IAAI,MAAM,CAAC;QACX,IAAI;YACF,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,IAAA,6BAAiB,EAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;SAC5D;QAAC,MAAM,GAAE;QACV,OAAO,MAAM,EAAE,OAAO,IAAI,MAAM,IAAI,IAAI,CAAC;KAC1C;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAxCD,0CAwCC","sourcesContent":["import fs from 'fs/promises';\nimport path from 'path';\nimport requireFromString from 'require-from-string';\nimport resolveFrom from 'resolve-from';\n\nimport type { RNConfigReactNativeConfig } from './reactNativeConfig.types';\nimport { fileExistsAsync } from './utils';\n\nlet tsMain: typeof import('typescript') | null | undefined = undefined;\n\n/**\n * Load the `react-native.config.js` or `react-native.config.ts` from the package.\n */\nexport async function loadConfigAsync<T extends RNConfigReactNativeConfig>(\n packageRoot: string\n): Promise<T | null> {\n const configJsPath = path.join(packageRoot, 'react-native.config.js');\n if (await fileExistsAsync(configJsPath)) {\n try {\n return require(configJsPath);\n } catch {\n return null;\n }\n }\n\n const configTsPath = path.join(packageRoot, 'react-native.config.ts');\n if (await fileExistsAsync(configTsPath)) {\n if (tsMain === undefined) {\n const tsPath = resolveFrom.silent(packageRoot, 'typescript');\n if (tsPath) {\n tsMain = require(tsPath);\n }\n } else if (tsMain == null) {\n return null;\n }\n\n const configContents = await fs.readFile(configTsPath, 'utf8');\n const transpiledContents = tsMain?.transpileModule(configContents, {\n compilerOptions: {\n module: tsMain.ModuleKind.NodeNext,\n moduleResolution: tsMain.ModuleResolutionKind.NodeNext,\n target: tsMain.ScriptTarget.ESNext,\n },\n });\n const outputText = transpiledContents?.outputText;\n let config;\n try {\n config = outputText ? requireFromString(outputText) : null;\n } catch {}\n return config?.default ?? config ?? null;\n }\n\n return null;\n}\n"]}

View File

@@ -0,0 +1,2 @@
export * from './reactNativeConfig';
export * from './reactNativeConfig.types';

View File

@@ -0,0 +1,19 @@
"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 });
__exportStar(require("./reactNativeConfig"), exports);
__exportStar(require("./reactNativeConfig.types"), exports);
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/reactNativeConfig/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,sDAAoC;AACpC,4DAA0C","sourcesContent":["export * from './reactNativeConfig';\nexport * from './reactNativeConfig.types';\n"]}

View File

@@ -0,0 +1,2 @@
import type { RNConfigDependencyIos, RNConfigReactNativePlatformsConfigIos } from './reactNativeConfig.types';
export declare function resolveDependencyConfigImplIosAsync(packageRoot: string, reactNativeConfig: RNConfigReactNativePlatformsConfigIos | null | undefined): Promise<RNConfigDependencyIos | null>;

View File

@@ -0,0 +1,31 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveDependencyConfigImplIosAsync = void 0;
const fast_glob_1 = __importDefault(require("fast-glob"));
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
async function resolveDependencyConfigImplIosAsync(packageRoot, reactNativeConfig) {
if (reactNativeConfig === null) {
// Skip autolinking for this package.
return null;
}
const podspecs = await (0, fast_glob_1.default)('*.podspec', { cwd: packageRoot });
if (!podspecs?.length) {
return null;
}
const mainPackagePodspec = path_1.default.basename(packageRoot) + '.podspec';
const podspecFile = podspecs.includes(mainPackagePodspec) ? mainPackagePodspec : podspecs[0];
const podspecPath = path_1.default.join(packageRoot, podspecFile);
const packageJson = JSON.parse(await promises_1.default.readFile(path_1.default.join(packageRoot, 'package.json'), 'utf8'));
return {
podspecPath,
version: packageJson.version,
configurations: reactNativeConfig?.configurations || [],
scriptPhases: reactNativeConfig?.scriptPhases || [],
};
}
exports.resolveDependencyConfigImplIosAsync = resolveDependencyConfigImplIosAsync;
//# sourceMappingURL=iosResolver.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"iosResolver.js","sourceRoot":"","sources":["../../src/reactNativeConfig/iosResolver.ts"],"names":[],"mappings":";;;;;;AAAA,0DAA6B;AAC7B,2DAA6B;AAC7B,gDAAwB;AAOjB,KAAK,UAAU,mCAAmC,CACvD,WAAmB,EACnB,iBAA2E;IAE3E,IAAI,iBAAiB,KAAK,IAAI,EAAE;QAC9B,qCAAqC;QACrC,OAAO,IAAI,CAAC;KACb;IAED,MAAM,QAAQ,GAAG,MAAM,IAAA,mBAAI,EAAC,WAAW,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC;IAC/D,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE;QACrB,OAAO,IAAI,CAAC;KACb;IACD,MAAM,kBAAkB,GAAG,cAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,UAAU,CAAC;IACnE,MAAM,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC7F,MAAM,WAAW,GAAG,cAAI,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IAExD,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,kBAAE,CAAC,QAAQ,CAAC,cAAI,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAElG,OAAO;QACL,WAAW;QACX,OAAO,EAAE,WAAW,CAAC,OAAO;QAC5B,cAAc,EAAE,iBAAiB,EAAE,cAAc,IAAI,EAAE;QACvD,YAAY,EAAE,iBAAiB,EAAE,YAAY,IAAI,EAAE;KACpD,CAAC;AACJ,CAAC;AAzBD,kFAyBC","sourcesContent":["import glob from 'fast-glob';\nimport fs from 'fs/promises';\nimport path from 'path';\n\nimport type {\n RNConfigDependencyIos,\n RNConfigReactNativePlatformsConfigIos,\n} from './reactNativeConfig.types';\n\nexport async function resolveDependencyConfigImplIosAsync(\n packageRoot: string,\n reactNativeConfig: RNConfigReactNativePlatformsConfigIos | null | undefined\n): Promise<RNConfigDependencyIos | null> {\n if (reactNativeConfig === null) {\n // Skip autolinking for this package.\n return null;\n }\n\n const podspecs = await glob('*.podspec', { cwd: packageRoot });\n if (!podspecs?.length) {\n return null;\n }\n const mainPackagePodspec = path.basename(packageRoot) + '.podspec';\n const podspecFile = podspecs.includes(mainPackagePodspec) ? mainPackagePodspec : podspecs[0];\n const podspecPath = path.join(packageRoot, podspecFile);\n\n const packageJson = JSON.parse(await fs.readFile(path.join(packageRoot, 'package.json'), 'utf8'));\n\n return {\n podspecPath,\n version: packageJson.version,\n configurations: reactNativeConfig?.configurations || [],\n scriptPhases: reactNativeConfig?.scriptPhases || [],\n };\n}\n"]}

View File

@@ -0,0 +1,12 @@
import type { RNConfigCommandOptions, RNConfigDependency, RNConfigReactNativeAppProjectConfig, RNConfigReactNativeProjectConfig, RNConfigResult } from './reactNativeConfig.types';
import type { SupportedPlatform } from '../types';
/**
* Create config for react-native core autolinking.
*/
export declare function createReactNativeConfigAsync({ platform, projectRoot, searchPaths, }: RNConfigCommandOptions): Promise<RNConfigResult>;
/**
* Find all dependencies and their directories from the project.
*/
export declare function findDependencyRootsAsync(projectRoot: string, searchPaths: string[]): Promise<Record<string, string>>;
export declare function resolveDependencyConfigAsync(platform: SupportedPlatform, name: string, packageRoot: string, projectConfig: RNConfigReactNativeProjectConfig | null): Promise<RNConfigDependency | null>;
export declare function resolveAppProjectConfigAsync(projectRoot: string, platform: SupportedPlatform): Promise<RNConfigReactNativeAppProjectConfig>;

View File

@@ -0,0 +1,117 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveAppProjectConfigAsync = exports.resolveDependencyConfigAsync = exports.findDependencyRootsAsync = exports.createReactNativeConfigAsync = void 0;
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
const androidResolver_1 = require("./androidResolver");
const config_1 = require("./config");
const iosResolver_1 = require("./iosResolver");
const utils_1 = require("./utils");
const utils_2 = require("../autolinking/utils");
/**
* Create config for react-native core autolinking.
*/
async function createReactNativeConfigAsync({ platform, projectRoot, searchPaths, }) {
const projectConfig = await (0, config_1.loadConfigAsync)(projectRoot);
const dependencyRoots = await findDependencyRootsAsync(projectRoot, searchPaths);
const reactNativePath = dependencyRoots['react-native'];
const dependencyConfigs = await Promise.all(Object.entries(dependencyRoots).map(async ([name, packageRoot]) => {
const config = await resolveDependencyConfigAsync(platform, name, packageRoot, projectConfig);
return [name, config];
}));
const dependencyResults = Object.fromEntries(dependencyConfigs.filter(([, config]) => config != null));
const projectData = await resolveAppProjectConfigAsync(projectRoot, platform);
return {
root: projectRoot,
reactNativePath,
dependencies: dependencyResults,
project: projectData,
};
}
exports.createReactNativeConfigAsync = createReactNativeConfigAsync;
/**
* Find all dependencies and their directories from the project.
*/
async function findDependencyRootsAsync(projectRoot, searchPaths) {
const packageJson = JSON.parse(await promises_1.default.readFile(path_1.default.join(projectRoot, 'package.json'), 'utf8'));
const dependencies = [
...Object.keys(packageJson.dependencies ?? {}),
...Object.keys(packageJson.devDependencies ?? {}),
];
const results = {};
// `searchPathSet` can be mutated to discover all "isolated modules groups", when using isolated modules
const searchPathSet = new Set(searchPaths);
for (const name of dependencies) {
for (const searchPath of searchPathSet) {
const packageConfigPath = path_1.default.resolve(searchPath, name, 'package.json');
if (await (0, utils_1.fileExistsAsync)(packageConfigPath)) {
const packageRoot = path_1.default.dirname(packageConfigPath);
results[name] = packageRoot;
const maybeIsolatedModulesPath = (0, utils_2.getIsolatedModulesPath)(packageRoot, name);
if (maybeIsolatedModulesPath) {
searchPathSet.add(maybeIsolatedModulesPath);
}
break;
}
}
}
return results;
}
exports.findDependencyRootsAsync = findDependencyRootsAsync;
async function resolveDependencyConfigAsync(platform, name, packageRoot, projectConfig) {
const libraryConfig = await (0, config_1.loadConfigAsync)(packageRoot);
const reactNativeConfig = {
...libraryConfig?.dependency,
...projectConfig?.dependencies[name],
};
if (Object.keys(libraryConfig?.platforms ?? {}).length > 0) {
// Package defines platforms would be a platform host package.
// The rnc-cli will skip this package.
// For example, the `react-native` package.
return null;
}
let platformData = null;
if (platform === 'android') {
platformData = await (0, androidResolver_1.resolveDependencyConfigImplAndroidAsync)(packageRoot, reactNativeConfig.platforms?.android);
}
else if (platform === 'ios') {
platformData = await (0, iosResolver_1.resolveDependencyConfigImplIosAsync)(packageRoot, reactNativeConfig.platforms?.ios);
}
if (!platformData) {
return null;
}
return {
root: packageRoot,
name,
platforms: {
[platform]: platformData,
},
};
}
exports.resolveDependencyConfigAsync = resolveDependencyConfigAsync;
async function resolveAppProjectConfigAsync(projectRoot, platform) {
if (platform === 'android') {
const androidDir = path_1.default.join(projectRoot, 'android');
const { gradle, manifest } = await (0, androidResolver_1.findGradleAndManifestAsync)({ androidDir, isLibrary: false });
const packageName = await (0, androidResolver_1.parsePackageNameAsync)(path_1.default.join(androidDir, manifest), path_1.default.join(androidDir, gradle));
return {
android: {
packageName: packageName ?? '',
sourceDir: path_1.default.join(projectRoot, 'android'),
},
};
}
if (platform === 'ios') {
return {
ios: {
sourceDir: path_1.default.join(projectRoot, 'ios'),
},
};
}
return {};
}
exports.resolveAppProjectConfigAsync = resolveAppProjectConfigAsync;
//# sourceMappingURL=reactNativeConfig.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,93 @@
import type { SupportedPlatform } from '../types';
/**
* Options for 'react-native-config' command.
*/
export interface RNConfigCommandOptions {
platform: SupportedPlatform;
projectRoot: string;
searchPaths: string[];
}
/**
* Dependency configuration for Android platform.
*/
export interface RNConfigDependencyAndroid {
sourceDir: string;
packageImportPath: string;
packageInstance: string;
dependencyConfiguration?: string;
buildTypes: string[];
libraryName?: string | null;
componentDescriptors?: string[] | null;
cmakeListsPath?: string | null;
cxxModuleCMakeListsModuleName?: string | null;
cxxModuleCMakeListsPath?: string | null;
cxxModuleHeaderName?: string | null;
}
/**
* Dependency configuration for iOS platform.
*/
export interface RNConfigDependencyIos {
podspecPath: string;
version: string;
configurations: string[];
scriptPhases: any[];
}
/**
* Dependency configuration.
*/
export interface RNConfigDependency {
root: string;
name: string;
platforms: {
android?: RNConfigDependencyAndroid;
ios?: RNConfigDependencyIos;
};
}
/**
* Result of 'react-native-config' command.
*/
export interface RNConfigResult {
root: string;
reactNativePath: string;
dependencies: Record<string, RNConfigDependency>;
project: {
ios?: {
sourceDir: string;
};
};
}
export type RNConfigReactNativePlatformsConfigAndroid = any;
export type RNConfigReactNativePlatformsConfigIos = any;
interface RNConfigReactNativePlatformsConfig {
platforms?: {
android?: RNConfigReactNativePlatformsConfigAndroid;
ios?: RNConfigReactNativePlatformsConfigIos;
};
}
/**
* The `react-native.config.js` config from projectRoot.
*/
export interface RNConfigReactNativeProjectConfig {
dependencies: Record<string, RNConfigReactNativePlatformsConfig>;
}
/**
* The `react-native.config.js` config from library packageRoot.
*/
export interface RNConfigReactNativeLibraryConfig {
dependency?: RNConfigReactNativePlatformsConfig;
platforms?: any;
}
export type RNConfigReactNativeConfig = RNConfigReactNativeProjectConfig | RNConfigReactNativeLibraryConfig;
/**
* The `project` config represents the app project configuration.
*/
export interface RNConfigReactNativeAppProjectConfig {
android?: {
sourceDir: string;
packageName: string;
};
ios?: {
sourceDir: string;
};
}
export {};

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=reactNativeConfig.types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"reactNativeConfig.types.js","sourceRoot":"","sources":["../../src/reactNativeConfig/reactNativeConfig.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { SupportedPlatform } from '../types';\n\n/**\n * Options for 'react-native-config' command.\n */\nexport interface RNConfigCommandOptions {\n platform: SupportedPlatform;\n projectRoot: string;\n searchPaths: string[];\n}\n\n/**\n * Dependency configuration for Android platform.\n */\nexport interface RNConfigDependencyAndroid {\n sourceDir: string;\n packageImportPath: string;\n packageInstance: string;\n dependencyConfiguration?: string;\n buildTypes: string[];\n libraryName?: string | null;\n componentDescriptors?: string[] | null;\n cmakeListsPath?: string | null;\n cxxModuleCMakeListsModuleName?: string | null;\n cxxModuleCMakeListsPath?: string | null;\n cxxModuleHeaderName?: string | null;\n}\n\n/**\n * Dependency configuration for iOS platform.\n */\nexport interface RNConfigDependencyIos {\n podspecPath: string;\n version: string;\n configurations: string[];\n scriptPhases: any[];\n}\n\n/**\n * Dependency configuration.\n */\nexport interface RNConfigDependency {\n root: string;\n name: string;\n platforms: {\n android?: RNConfigDependencyAndroid;\n ios?: RNConfigDependencyIos;\n };\n}\n\n/**\n * Result of 'react-native-config' command.\n */\nexport interface RNConfigResult {\n root: string;\n reactNativePath: string;\n dependencies: Record<string, RNConfigDependency>;\n project: {\n ios?: {\n sourceDir: string;\n };\n };\n}\n\nexport type RNConfigReactNativePlatformsConfigAndroid = any;\nexport type RNConfigReactNativePlatformsConfigIos = any;\n\ninterface RNConfigReactNativePlatformsConfig {\n platforms?: {\n android?: RNConfigReactNativePlatformsConfigAndroid;\n ios?: RNConfigReactNativePlatformsConfigIos;\n };\n}\n\n/**\n * The `react-native.config.js` config from projectRoot.\n */\nexport interface RNConfigReactNativeProjectConfig {\n dependencies: Record<string, RNConfigReactNativePlatformsConfig>;\n}\n\n/**\n * The `react-native.config.js` config from library packageRoot.\n */\nexport interface RNConfigReactNativeLibraryConfig {\n dependency?: RNConfigReactNativePlatformsConfig;\n platforms?: any;\n}\n\nexport type RNConfigReactNativeConfig =\n | RNConfigReactNativeProjectConfig\n | RNConfigReactNativeLibraryConfig;\n\n/**\n * The `project` config represents the app project configuration.\n */\nexport interface RNConfigReactNativeAppProjectConfig {\n android?: {\n sourceDir: string;\n packageName: string;\n };\n ios?: {\n sourceDir: string;\n };\n}\n"]}

View File

@@ -0,0 +1,20 @@
/// <reference types="node" />
import glob from 'fast-glob';
type GlobOptions = Parameters<typeof glob>[1];
/**
* A matching function that takes a file path and its contents and returns a string if it matches, or null otherwise.
*/
type MatchFunctor = (filePath: string, contents: Buffer) => string | null;
/**
* Check if the file exists.
*/
export declare function fileExistsAsync(file: string): Promise<boolean>;
/**
* Search files that match the glob pattern and return all matches from the matchFunctor.
*/
export declare function globMatchFunctorAllAsync(globPattern: string, matchFunctor: MatchFunctor, options?: GlobOptions): Promise<string[]>;
/**
* Search files that match the glob pattern and return the first match from the matchFunctor.
*/
export declare function globMatchFunctorFirstAsync(globPattern: string, matchFunctor: MatchFunctor, options?: GlobOptions): Promise<string | null>;
export {};

View File

@@ -0,0 +1,58 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.globMatchFunctorFirstAsync = exports.globMatchFunctorAllAsync = exports.fileExistsAsync = void 0;
const fast_glob_1 = __importDefault(require("fast-glob"));
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
/**
* Check if the file exists.
*/
async function fileExistsAsync(file) {
return (await promises_1.default.stat(file).catch(() => null))?.isFile() ?? false;
}
exports.fileExistsAsync = fileExistsAsync;
/**
* Search files that match the glob pattern and return all matches from the matchFunctor.
*/
async function globMatchFunctorAllAsync(globPattern, matchFunctor, options) {
const globStream = fast_glob_1.default.stream(globPattern, options);
const cwd = options?.cwd ?? process.cwd();
const results = [];
for await (const file of globStream) {
let filePath = file.toString();
if (!path_1.default.isAbsolute(filePath)) {
filePath = path_1.default.resolve(cwd, filePath);
}
const contents = await promises_1.default.readFile(filePath);
const matched = matchFunctor(filePath, contents);
if (matched != null) {
results.push(matched);
}
}
return results;
}
exports.globMatchFunctorAllAsync = globMatchFunctorAllAsync;
/**
* Search files that match the glob pattern and return the first match from the matchFunctor.
*/
async function globMatchFunctorFirstAsync(globPattern, matchFunctor, options) {
const globStream = fast_glob_1.default.stream(globPattern, options);
const cwd = options?.cwd ?? process.cwd();
for await (const file of globStream) {
let filePath = file.toString();
if (!path_1.default.isAbsolute(filePath)) {
filePath = path_1.default.resolve(cwd, filePath);
}
const contents = await promises_1.default.readFile(filePath);
const matched = matchFunctor(filePath, contents);
if (matched != null) {
return matched;
}
}
return null;
}
exports.globMatchFunctorFirstAsync = globMatchFunctorFirstAsync;
//# sourceMappingURL=utils.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/reactNativeConfig/utils.ts"],"names":[],"mappings":";;;;;;AAAA,0DAA6B;AAC7B,2DAA6B;AAC7B,gDAAwB;AASxB;;GAEG;AACI,KAAK,UAAU,eAAe,CAAC,IAAY;IAChD,OAAO,CAAC,MAAM,kBAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,KAAK,CAAC;AACpE,CAAC;AAFD,0CAEC;AAED;;GAEG;AACI,KAAK,UAAU,wBAAwB,CAC5C,WAAmB,EACnB,YAA0B,EAC1B,OAAqB;IAErB,MAAM,UAAU,GAAG,mBAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IACrD,MAAM,GAAG,GAAG,OAAO,EAAE,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1C,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,UAAU,EAAE;QACnC,IAAI,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,cAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;YAC9B,QAAQ,GAAG,cAAI,CAAC,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;SACxC;QACD,MAAM,QAAQ,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACjD,IAAI,OAAO,IAAI,IAAI,EAAE;YACnB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;SACvB;KACF;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AApBD,4DAoBC;AAED;;GAEG;AACI,KAAK,UAAU,0BAA0B,CAC9C,WAAmB,EACnB,YAA0B,EAC1B,OAAqB;IAErB,MAAM,UAAU,GAAG,mBAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IACrD,MAAM,GAAG,GAAG,OAAO,EAAE,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1C,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,UAAU,EAAE;QACnC,IAAI,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,cAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;YAC9B,QAAQ,GAAG,cAAI,CAAC,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;SACxC;QACD,MAAM,QAAQ,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACjD,IAAI,OAAO,IAAI,IAAI,EAAE;YACnB,OAAO,OAAO,CAAC;SAChB;KACF;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAnBD,gEAmBC","sourcesContent":["import glob from 'fast-glob';\nimport fs from 'fs/promises';\nimport path from 'path';\n\ntype GlobOptions = Parameters<typeof glob>[1];\n\n/**\n * A matching function that takes a file path and its contents and returns a string if it matches, or null otherwise.\n */\ntype MatchFunctor = (filePath: string, contents: Buffer) => string | null;\n\n/**\n * Check if the file exists.\n */\nexport async function fileExistsAsync(file: string): Promise<boolean> {\n return (await fs.stat(file).catch(() => null))?.isFile() ?? false;\n}\n\n/**\n * Search files that match the glob pattern and return all matches from the matchFunctor.\n */\nexport async function globMatchFunctorAllAsync(\n globPattern: string,\n matchFunctor: MatchFunctor,\n options?: GlobOptions\n): Promise<string[]> {\n const globStream = glob.stream(globPattern, options);\n const cwd = options?.cwd ?? process.cwd();\n const results: string[] = [];\n for await (const file of globStream) {\n let filePath = file.toString();\n if (!path.isAbsolute(filePath)) {\n filePath = path.resolve(cwd, filePath);\n }\n const contents = await fs.readFile(filePath);\n const matched = matchFunctor(filePath, contents);\n if (matched != null) {\n results.push(matched);\n }\n }\n return results;\n}\n\n/**\n * Search files that match the glob pattern and return the first match from the matchFunctor.\n */\nexport async function globMatchFunctorFirstAsync(\n globPattern: string,\n matchFunctor: MatchFunctor,\n options?: GlobOptions\n): Promise<string | null> {\n const globStream = glob.stream(globPattern, options);\n const cwd = options?.cwd ?? process.cwd();\n for await (const file of globStream) {\n let filePath = file.toString();\n if (!path.isAbsolute(filePath)) {\n filePath = path.resolve(cwd, filePath);\n }\n const contents = await fs.readFile(filePath);\n const matched = matchFunctor(filePath, contents);\n if (matched != null) {\n return matched;\n }\n }\n return null;\n}\n"]}