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,21 @@
MIT License
Copyright (c) 2018 react-native-community
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.

View File

@@ -0,0 +1,70 @@
# @react-native-community/cli-hermes
This package is part of the [React Native CLI](../../README.md). It contains commands for managing the Hermes engine.
## Installation
```sh
yarn add @react-native-community/cli-hermes
```
## Commands
### `profile-hermes`
Usage:
```sh
npx react-native profile-hermes [destinationDir] <flag>
```
Pull and convert a Hermes tracing profile to Chrome tracing profile, then store it in the directory <destinationDir> of the local machine.
- `destinationDir` is optional, if provided, pull the file to that directory
> default: pull to the current React Native app root directory
#### Options
#### `--filename <string>`
File name of the profile to be downloaded, eg. sampling-profiler-trace8593107139682635366.cpuprofile.
> default: pull the latest file
#### `--raw`
Pulls the original Hermes tracing profile without any transformation
#### `--sourcemap-path <string>`
The local path to your source map file if you generated it manually, ex. `/tmp/sourcemap.json`
#### `--generate-sourcemap`
Generate the JS bundle and source map in `os.tmpdir()`
#### `--port <number>`
The running metro server port number
> default: 8081
#### `--appId <string>`
Specify an `applicationId` to launch after build. If not specified, `package` from AndroidManifest.xml will be used.
#### `--appIdSuffix <string>`
Specify an `applicationIdSuffix` to launch after build.
#### `--host <string>`
The host of the packager.
> default: localhost
### Notes on source map
This step is recommended in order for the source map to be generated:
If you are planning on building a debug APK, that will run without the packager, by invoking `./gradlew assembleDebug` you can simply set `bundleInDebug: true` in your app/build.gradle file, inside the `project.ext.react` map.

View File

@@ -0,0 +1,3 @@
import profileHermes from './profileHermes/index';
export default profileHermes;
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,uBAAuB,CAAC;AAClD,eAAe,aAAa,CAAC"}

View File

@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _index = _interopRequireDefault(require("./profileHermes/index"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var _default = _index.default;
exports.default = _default;
//# sourceMappingURL=index.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["profileHermes"],"sources":["../src/index.ts"],"sourcesContent":["import profileHermes from './profileHermes/index';\nexport default profileHermes;\n"],"mappings":";;;;;;AAAA;AAAkD;AAAA,eACnCA,cAAa;AAAA"}

View File

@@ -0,0 +1,14 @@
import { Config } from '@react-native-community/cli-types';
/**
* Pull and convert a Hermes tracing profile to Chrome tracing profile
* @param ctx
* @param dstPath
* @param fileName
* @param sourceMapPath
* @param raw
* @param generateSourceMap
* @param appId
* @param appIdSuffix
*/
export declare function downloadProfile(ctx: Config, dstPath: string, filename?: string, sourcemapPath?: string, raw?: boolean, shouldGenerateSourcemap?: boolean, port?: string, appId?: string, appIdSuffix?: string, host?: string): Promise<void>;
//# sourceMappingURL=downloadProfile.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"downloadProfile.d.ts","sourceRoot":"","sources":["../../src/profileHermes/downloadProfile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAwCzD;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,EACjB,aAAa,CAAC,EAAE,MAAM,EACtB,GAAG,CAAC,EAAE,OAAO,EACb,uBAAuB,CAAC,EAAE,OAAO,EACjC,IAAI,GAAE,MAAe,EACrB,KAAK,CAAC,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,MAAM,EACpB,IAAI,GAAE,MAAoB,iBAyF3B"}

View File

@@ -0,0 +1,158 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.downloadProfile = downloadProfile;
function _child_process() {
const data = require("child_process");
_child_process = function () {
return data;
};
return data;
}
function _cliTools() {
const data = require("@react-native-community/cli-tools");
_cliTools = function () {
return data;
};
return data;
}
function _fs() {
const data = _interopRequireDefault(require("fs"));
_fs = function () {
return data;
};
return data;
}
function _path() {
const data = _interopRequireDefault(require("path"));
_path = function () {
return data;
};
return data;
}
function _os() {
const data = _interopRequireDefault(require("os"));
_os = function () {
return data;
};
return data;
}
function _hermesProfileTransformer() {
const data = _interopRequireDefault(require("hermes-profile-transformer"));
_hermesProfileTransformer = function () {
return data;
};
return data;
}
var _sourcemapUtils = require("./sourcemapUtils");
function _cliPlatformAndroid() {
const data = require("@react-native-community/cli-platform-android");
_cliPlatformAndroid = function () {
return data;
};
return data;
}
var _metroBundleOptions = require("./metroBundleOptions");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Get the last modified hermes profile
* @param packageNameWithSuffix
*/
function getLatestFile(packageNameWithSuffix) {
try {
const file = (0, _child_process().execSync)(`adb shell run-as ${packageNameWithSuffix} ls cache/ -tp | grep -v /$ | grep -E '.cpuprofile' | head -1
`);
return file.toString().trim();
} catch (e) {
throw e;
}
}
function execSyncWithLog(command) {
_cliTools().logger.debug(`${command}`);
return (0, _child_process().execSync)(command);
}
/**
* A wrapper that converts an object to JSON with 4 spaces for indentation.
*
* @param obj Any object that can be represented as JSON
* @returns A JSON string
*/
function jsonStringify(obj) {
return JSON.stringify(obj, undefined, 4);
}
/**
* Pull and convert a Hermes tracing profile to Chrome tracing profile
* @param ctx
* @param dstPath
* @param fileName
* @param sourceMapPath
* @param raw
* @param generateSourceMap
* @param appId
* @param appIdSuffix
*/
async function downloadProfile(ctx, dstPath, filename, sourcemapPath, raw, shouldGenerateSourcemap, port = '8081', appId, appIdSuffix, host = 'localhost') {
try {
const androidProject = (0, _cliPlatformAndroid().getAndroidProject)(ctx);
const packageNameWithSuffix = [appId || androidProject.packageName, appIdSuffix].filter(Boolean).join('.');
// If file name is not specified, pull the latest file from device
const file = filename || getLatestFile(packageNameWithSuffix);
if (!file) {
throw new (_cliTools().CLIError)('There is no file in the cache/ directory. Did you record a profile from the developer menu?');
}
_cliTools().logger.info(`File to be pulled: ${file}`);
// If destination path is not specified, pull to the current directory
dstPath = dstPath || ctx.root;
_cliTools().logger.debug('Internal commands run to pull the file:');
// If --raw, pull the hermes profile to dstPath
if (raw) {
execSyncWithLog(`adb shell run-as ${packageNameWithSuffix} cat cache/${file} > ${dstPath}/${file}`);
_cliTools().logger.success(`Successfully pulled the file to ${dstPath}/${file}`);
}
// Else: transform the profile to Chrome format and pull it to dstPath
else {
const osTmpDir = _os().default.tmpdir();
const tempFilePath = _path().default.join(osTmpDir, file);
execSyncWithLog(`adb shell run-as ${packageNameWithSuffix} cat cache/${file} > ${tempFilePath}`);
const bundleOptions = (0, _metroBundleOptions.getMetroBundleOptions)(tempFilePath, host);
// If path to source map is not given
if (!sourcemapPath) {
// Get or generate the source map
if (shouldGenerateSourcemap) {
sourcemapPath = await (0, _sourcemapUtils.generateSourcemap)(port, bundleOptions);
} else {
sourcemapPath = await (0, _sourcemapUtils.findSourcemap)(ctx, port, bundleOptions);
}
// Run without source map
if (!sourcemapPath) {
_cliTools().logger.warn('Cannot find source maps, running the transformer without it');
_cliTools().logger.info('Instructions on how to get source maps: set `bundleInDebug: true` in your app/build.gradle file, inside the `project.ext.react` map.');
}
}
// Run transformer tool to convert from Hermes to Chrome format
const events = await (0, _hermesProfileTransformer().default)(tempFilePath, sourcemapPath, 'index.bundle');
const transformedFilePath = `${dstPath}/${_path().default.basename(file, '.cpuprofile')}-converted.json`;
// Convert to JSON in chunks because JSON.stringify() will fail for large
// arrays with the error "RangeError: Invalid string length"
const out = events.map(jsonStringify).join(',');
_fs().default.writeFileSync(transformedFilePath, '[' + out + ']', 'utf-8');
_cliTools().logger.success(`Successfully converted to Chrome tracing format and pulled the file to ${transformedFilePath}`);
}
} catch (e) {
throw e;
}
}
//# sourceMappingURL=downloadProfile.ts.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,36 @@
import { Config } from '@react-native-community/cli-types';
type Options = {
filename?: string;
raw?: boolean;
sourcemapPath?: string;
generateSourcemap?: boolean;
port: string;
appId?: string;
appIdSuffix?: string;
host?: string;
};
declare function profileHermes([dstPath]: Array<string>, ctx: Config, options: Options): Promise<void>;
declare const _default: {
name: string;
description: string;
func: typeof profileHermes;
options: ({
name: string;
description: string;
default?: undefined;
} | {
name: string;
default: string;
description?: undefined;
} | {
name: string;
description: string;
default: string;
})[];
examples: {
desc: string;
cmd: string;
}[];
};
export default _default;
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/profileHermes/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAGzD,KAAK,OAAO,GAAG;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,iBAAe,aAAa,CAC1B,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,EACxB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,OAAO,iBAwBjB;;;;;;;;;;;;;;;;;;;;;;;AAED,wBAkDE"}

View File

@@ -0,0 +1,63 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
function _cliTools() {
const data = require("@react-native-community/cli-tools");
_cliTools = function () {
return data;
};
return data;
}
var _downloadProfile = require("./downloadProfile");
async function profileHermes([dstPath], ctx, options) {
try {
_cliTools().logger.info('Downloading a Hermes Sampling Profiler from your Android device...');
if (!options.filename) {
_cliTools().logger.info('No filename is provided, pulling latest file');
}
await (0, _downloadProfile.downloadProfile)(ctx, dstPath, options.filename, options.sourcemapPath, options.raw, options.generateSourcemap, options.port, options.appId, options.appIdSuffix, options.host);
} catch (err) {
throw err;
}
}
var _default = {
name: 'profile-hermes [destinationDir]',
description: 'Pull and convert a Hermes tracing profile to Chrome tracing profile, then store it in the directory <destinationDir> of the local machine',
func: profileHermes,
options: [{
name: '--filename <string>',
description: 'File name of the profile to be downloaded, eg. sampling-profiler-trace8593107139682635366.cpuprofile'
}, {
name: '--raw',
description: 'Pulls the original Hermes tracing profile without any transformation'
}, {
name: '--sourcemap-path <string>',
description: 'The local path to your source map file, eg. /tmp/sourcemap.json'
}, {
name: '--generate-sourcemap',
description: 'Generates the JS bundle and source map'
}, {
name: '--port <number>',
default: `${process.env.RCT_METRO_PORT || 8081}`
}, {
name: '--appId <string>',
description: 'Specify an applicationId to launch after build. If not specified, `package` from AndroidManifest.xml will be used.'
}, {
name: '--appIdSuffix <string>',
description: 'Specify an applicationIdSuffix to launch after build.'
}, {
name: '--host <string>',
description: 'The host of the packager.',
default: 'localhost'
}],
examples: [{
desc: 'Download the Hermes Sampling Profiler to the directory <destinationDir> on the local machine',
cmd: 'profile-hermes /tmp'
}]
};
exports.default = _default;
//# sourceMappingURL=index.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["profileHermes","dstPath","ctx","options","logger","info","filename","downloadProfile","sourcemapPath","raw","generateSourcemap","port","appId","appIdSuffix","host","err","name","description","func","default","process","env","RCT_METRO_PORT","examples","desc","cmd"],"sources":["../../src/profileHermes/index.ts"],"sourcesContent":["import {logger, CLIError} from '@react-native-community/cli-tools';\nimport {Config} from '@react-native-community/cli-types';\nimport {downloadProfile} from './downloadProfile';\n\ntype Options = {\n filename?: string;\n raw?: boolean;\n sourcemapPath?: string;\n generateSourcemap?: boolean;\n port: string;\n appId?: string;\n appIdSuffix?: string;\n host?: string;\n};\n\nasync function profileHermes(\n [dstPath]: Array<string>,\n ctx: Config,\n options: Options,\n) {\n try {\n logger.info(\n 'Downloading a Hermes Sampling Profiler from your Android device...',\n );\n if (!options.filename) {\n logger.info('No filename is provided, pulling latest file');\n }\n await downloadProfile(\n ctx,\n dstPath,\n options.filename,\n options.sourcemapPath,\n options.raw,\n options.generateSourcemap,\n options.port,\n options.appId,\n options.appIdSuffix,\n options.host,\n );\n } catch (err) {\n throw err as CLIError;\n }\n}\n\nexport default {\n name: 'profile-hermes [destinationDir]',\n description:\n 'Pull and convert a Hermes tracing profile to Chrome tracing profile, then store it in the directory <destinationDir> of the local machine',\n func: profileHermes,\n options: [\n {\n name: '--filename <string>',\n description:\n 'File name of the profile to be downloaded, eg. sampling-profiler-trace8593107139682635366.cpuprofile',\n },\n {\n name: '--raw',\n description:\n 'Pulls the original Hermes tracing profile without any transformation',\n },\n {\n name: '--sourcemap-path <string>',\n description:\n 'The local path to your source map file, eg. /tmp/sourcemap.json',\n },\n {\n name: '--generate-sourcemap',\n description: 'Generates the JS bundle and source map',\n },\n {\n name: '--port <number>',\n default: `${process.env.RCT_METRO_PORT || 8081}`,\n },\n {\n name: '--appId <string>',\n description:\n 'Specify an applicationId to launch after build. If not specified, `package` from AndroidManifest.xml will be used.',\n },\n {\n name: '--appIdSuffix <string>',\n description: 'Specify an applicationIdSuffix to launch after build.',\n },\n {\n name: '--host <string>',\n description: 'The host of the packager.',\n default: 'localhost',\n },\n ],\n examples: [\n {\n desc: 'Download the Hermes Sampling Profiler to the directory <destinationDir> on the local machine',\n cmd: 'profile-hermes /tmp',\n },\n ],\n};\n"],"mappings":";;;;;;AAAA;EAAA;EAAA;IAAA;EAAA;EAAA;AAAA;AAEA;AAaA,eAAeA,aAAa,CAC1B,CAACC,OAAO,CAAgB,EACxBC,GAAW,EACXC,OAAgB,EAChB;EACA,IAAI;IACFC,kBAAM,CAACC,IAAI,CACT,oEAAoE,CACrE;IACD,IAAI,CAACF,OAAO,CAACG,QAAQ,EAAE;MACrBF,kBAAM,CAACC,IAAI,CAAC,8CAA8C,CAAC;IAC7D;IACA,MAAM,IAAAE,gCAAe,EACnBL,GAAG,EACHD,OAAO,EACPE,OAAO,CAACG,QAAQ,EAChBH,OAAO,CAACK,aAAa,EACrBL,OAAO,CAACM,GAAG,EACXN,OAAO,CAACO,iBAAiB,EACzBP,OAAO,CAACQ,IAAI,EACZR,OAAO,CAACS,KAAK,EACbT,OAAO,CAACU,WAAW,EACnBV,OAAO,CAACW,IAAI,CACb;EACH,CAAC,CAAC,OAAOC,GAAG,EAAE;IACZ,MAAMA,GAAG;EACX;AACF;AAAC,eAEc;EACbC,IAAI,EAAE,iCAAiC;EACvCC,WAAW,EACT,2IAA2I;EAC7IC,IAAI,EAAElB,aAAa;EACnBG,OAAO,EAAE,CACP;IACEa,IAAI,EAAE,qBAAqB;IAC3BC,WAAW,EACT;EACJ,CAAC,EACD;IACED,IAAI,EAAE,OAAO;IACbC,WAAW,EACT;EACJ,CAAC,EACD;IACED,IAAI,EAAE,2BAA2B;IACjCC,WAAW,EACT;EACJ,CAAC,EACD;IACED,IAAI,EAAE,sBAAsB;IAC5BC,WAAW,EAAE;EACf,CAAC,EACD;IACED,IAAI,EAAE,iBAAiB;IACvBG,OAAO,EAAG,GAAEC,OAAO,CAACC,GAAG,CAACC,cAAc,IAAI,IAAK;EACjD,CAAC,EACD;IACEN,IAAI,EAAE,kBAAkB;IACxBC,WAAW,EACT;EACJ,CAAC,EACD;IACED,IAAI,EAAE,wBAAwB;IAC9BC,WAAW,EAAE;EACf,CAAC,EACD;IACED,IAAI,EAAE,iBAAiB;IACvBC,WAAW,EAAE,2BAA2B;IACxCE,OAAO,EAAE;EACX,CAAC,CACF;EACDI,QAAQ,EAAE,CACR;IACEC,IAAI,EAAE,8FAA8F;IACpGC,GAAG,EAAE;EACP,CAAC;AAEL,CAAC;AAAA"}

View File

@@ -0,0 +1,8 @@
export interface MetroBundleOptions {
platform: string;
dev: boolean;
minify: boolean;
host: string;
}
export declare function getMetroBundleOptions(downloadedProfileFilePath: string, host: string): MetroBundleOptions;
//# sourceMappingURL=metroBundleOptions.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"metroBundleOptions.d.ts","sourceRoot":"","sources":["../../src/profileHermes/metroBundleOptions.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,qBAAqB,CACnC,yBAAyB,EAAE,MAAM,EACjC,IAAI,EAAE,MAAM,GACX,kBAAkB,CAmDpB"}

View File

@@ -0,0 +1,70 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getMetroBundleOptions = getMetroBundleOptions;
function _cliTools() {
const data = require("@react-native-community/cli-tools");
_cliTools = function () {
return data;
};
return data;
}
function _fs() {
const data = _interopRequireDefault(require("fs"));
_fs = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function getMetroBundleOptions(downloadedProfileFilePath, host) {
let options = {
platform: 'android',
dev: true,
minify: false,
host
};
try {
const contents = JSON.parse(_fs().default.readFileSync(downloadedProfileFilePath, {
encoding: 'utf8'
}));
const matchBundleUrl = /^.*\((.*index\.bundle.*)\)/;
let containsExpoDevMenu = false;
let hadMatch = false;
for (const frame of Object.values(contents.stackFrames)) {
if (frame.name.includes('EXDevMenuApp')) {
containsExpoDevMenu = true;
}
const match = matchBundleUrl.exec(frame.name);
if (match) {
const parsed = new URL(match[1]);
const platform = parsed.searchParams.get('platform'),
dev = parsed.searchParams.get('dev'),
minify = parsed.searchParams.get('minify');
if (platform) {
options.platform = platform;
}
if (dev) {
options.dev = dev === 'true';
}
if (minify) {
options.minify = minify === 'true';
}
hadMatch = true;
break;
}
}
if (containsExpoDevMenu && !hadMatch) {
_cliTools().logger.warn(`Found references to the Expo Dev Menu in your profiling sample.
You might have accidentally recorded the Expo Dev Menu instead of your own application.
To work around this, please reload your app twice before starting a profiler recording.`);
}
} catch (e) {
throw e;
}
return options;
}
//# sourceMappingURL=metroBundleOptions.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["getMetroBundleOptions","downloadedProfileFilePath","host","options","platform","dev","minify","contents","JSON","parse","fs","readFileSync","encoding","matchBundleUrl","containsExpoDevMenu","hadMatch","frame","Object","values","stackFrames","name","includes","match","exec","parsed","URL","searchParams","get","logger","warn","e"],"sources":["../../src/profileHermes/metroBundleOptions.ts"],"sourcesContent":["import {logger} from '@react-native-community/cli-tools';\nimport fs from 'fs';\nimport type {HermesCPUProfile} from 'hermes-profile-transformer/dist/types/HermesProfile';\n\nexport interface MetroBundleOptions {\n platform: string;\n dev: boolean;\n minify: boolean;\n host: string;\n}\n\nexport function getMetroBundleOptions(\n downloadedProfileFilePath: string,\n host: string,\n): MetroBundleOptions {\n let options: MetroBundleOptions = {\n platform: 'android',\n dev: true,\n minify: false,\n host,\n };\n\n try {\n const contents: HermesCPUProfile = JSON.parse(\n fs.readFileSync(downloadedProfileFilePath, {\n encoding: 'utf8',\n }),\n );\n const matchBundleUrl = /^.*\\((.*index\\.bundle.*)\\)/;\n let containsExpoDevMenu = false;\n let hadMatch = false;\n for (const frame of Object.values(contents.stackFrames)) {\n if (frame.name.includes('EXDevMenuApp')) {\n containsExpoDevMenu = true;\n }\n const match = matchBundleUrl.exec(frame.name);\n if (match) {\n const parsed = new URL(match[1]);\n const platform = parsed.searchParams.get('platform'),\n dev = parsed.searchParams.get('dev'),\n minify = parsed.searchParams.get('minify');\n if (platform) {\n options.platform = platform;\n }\n if (dev) {\n options.dev = dev === 'true';\n }\n if (minify) {\n options.minify = minify === 'true';\n }\n\n hadMatch = true;\n break;\n }\n }\n if (containsExpoDevMenu && !hadMatch) {\n logger.warn(`Found references to the Expo Dev Menu in your profiling sample.\nYou might have accidentally recorded the Expo Dev Menu instead of your own application.\nTo work around this, please reload your app twice before starting a profiler recording.`);\n }\n } catch (e) {\n throw e;\n }\n\n return options;\n}\n"],"mappings":";;;;;;AAAA;EAAA;EAAA;IAAA;EAAA;EAAA;AAAA;AACA;EAAA;EAAA;IAAA;EAAA;EAAA;AAAA;AAAoB;AAUb,SAASA,qBAAqB,CACnCC,yBAAiC,EACjCC,IAAY,EACQ;EACpB,IAAIC,OAA2B,GAAG;IAChCC,QAAQ,EAAE,SAAS;IACnBC,GAAG,EAAE,IAAI;IACTC,MAAM,EAAE,KAAK;IACbJ;EACF,CAAC;EAED,IAAI;IACF,MAAMK,QAA0B,GAAGC,IAAI,CAACC,KAAK,CAC3CC,aAAE,CAACC,YAAY,CAACV,yBAAyB,EAAE;MACzCW,QAAQ,EAAE;IACZ,CAAC,CAAC,CACH;IACD,MAAMC,cAAc,GAAG,4BAA4B;IACnD,IAAIC,mBAAmB,GAAG,KAAK;IAC/B,IAAIC,QAAQ,GAAG,KAAK;IACpB,KAAK,MAAMC,KAAK,IAAIC,MAAM,CAACC,MAAM,CAACX,QAAQ,CAACY,WAAW,CAAC,EAAE;MACvD,IAAIH,KAAK,CAACI,IAAI,CAACC,QAAQ,CAAC,cAAc,CAAC,EAAE;QACvCP,mBAAmB,GAAG,IAAI;MAC5B;MACA,MAAMQ,KAAK,GAAGT,cAAc,CAACU,IAAI,CAACP,KAAK,CAACI,IAAI,CAAC;MAC7C,IAAIE,KAAK,EAAE;QACT,MAAME,MAAM,GAAG,IAAIC,GAAG,CAACH,KAAK,CAAC,CAAC,CAAC,CAAC;QAChC,MAAMlB,QAAQ,GAAGoB,MAAM,CAACE,YAAY,CAACC,GAAG,CAAC,UAAU,CAAC;UAClDtB,GAAG,GAAGmB,MAAM,CAACE,YAAY,CAACC,GAAG,CAAC,KAAK,CAAC;UACpCrB,MAAM,GAAGkB,MAAM,CAACE,YAAY,CAACC,GAAG,CAAC,QAAQ,CAAC;QAC5C,IAAIvB,QAAQ,EAAE;UACZD,OAAO,CAACC,QAAQ,GAAGA,QAAQ;QAC7B;QACA,IAAIC,GAAG,EAAE;UACPF,OAAO,CAACE,GAAG,GAAGA,GAAG,KAAK,MAAM;QAC9B;QACA,IAAIC,MAAM,EAAE;UACVH,OAAO,CAACG,MAAM,GAAGA,MAAM,KAAK,MAAM;QACpC;QAEAS,QAAQ,GAAG,IAAI;QACf;MACF;IACF;IACA,IAAID,mBAAmB,IAAI,CAACC,QAAQ,EAAE;MACpCa,kBAAM,CAACC,IAAI,CAAE;AACnB;AACA,wFAAwF,CAAC;IACrF;EACF,CAAC,CAAC,OAAOC,CAAC,EAAE;IACV,MAAMA,CAAC;EACT;EAEA,OAAO3B,OAAO;AAChB"}

View File

@@ -0,0 +1,12 @@
import { Config } from '@react-native-community/cli-types';
import { MetroBundleOptions } from './metroBundleOptions';
/**
* Generate a sourcemap by fetching it from a running metro server
*/
export declare function generateSourcemap(port: string, bundleOptions: MetroBundleOptions): Promise<string | undefined>;
/**
*
* @param ctx
*/
export declare function findSourcemap(ctx: Config, port: string, bundleOptions: MetroBundleOptions): Promise<string | undefined>;
//# sourceMappingURL=sourcemapUtils.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"sourcemapUtils.d.ts","sourceRoot":"","sources":["../../src/profileHermes/sourcemapUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAMzD,OAAO,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AAyCxD;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,kBAAkB,GAChC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAgB7B;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,kBAAkB,GAChC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAkC7B"}

View File

@@ -0,0 +1,109 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.findSourcemap = findSourcemap;
exports.generateSourcemap = generateSourcemap;
function _cliTools() {
const data = require("@react-native-community/cli-tools");
_cliTools = function () {
return data;
};
return data;
}
function _fs() {
const data = _interopRequireDefault(require("fs"));
_fs = function () {
return data;
};
return data;
}
function _path() {
const data = _interopRequireDefault(require("path"));
_path = function () {
return data;
};
return data;
}
function _os() {
const data = _interopRequireDefault(require("os"));
_os = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function getTempFilePath(filename) {
return _path().default.join(_os().default.tmpdir(), filename);
}
function writeJsonSync(targetPath, data) {
let json;
try {
json = JSON.stringify(data);
} catch (e) {
throw new (_cliTools().CLIError)(`Failed to serialize data to json before writing to ${targetPath}`, e);
}
try {
_fs().default.writeFileSync(targetPath, json, 'utf-8');
} catch (e) {
throw new (_cliTools().CLIError)(`Failed to write json to ${targetPath}`, e);
}
}
async function getSourcemapFromServer(port, {
platform,
dev,
minify,
host
}) {
_cliTools().logger.debug('Getting source maps from Metro packager server');
const requestURL = `http://${host}:${port}/index.map?platform=${platform}&dev=${dev}&minify=${minify}`;
_cliTools().logger.debug(`Downloading from ${requestURL}`);
try {
const {
data
} = await (0, _cliTools().fetch)(requestURL);
return data;
} catch (e) {
_cliTools().logger.debug(`Failed to fetch source map from "${requestURL}"`);
return undefined;
}
}
/**
* Generate a sourcemap by fetching it from a running metro server
*/
async function generateSourcemap(port, bundleOptions) {
// Fetch the source map to a temp directory
const sourceMapPath = getTempFilePath('index.map');
const sourceMapResult = await getSourcemapFromServer(port, bundleOptions);
if (sourceMapResult) {
_cliTools().logger.debug('Using source maps from Metro packager server');
writeJsonSync(sourceMapPath, sourceMapResult);
_cliTools().logger.debug(`Successfully obtained the source map and stored it in ${sourceMapPath}`);
return sourceMapPath;
} else {
_cliTools().logger.error('Cannot obtain source maps from Metro packager server');
return undefined;
}
}
/**
*
* @param ctx
*/
async function findSourcemap(ctx, port, bundleOptions) {
const intermediateBuildPath = _path().default.join(ctx.root, 'android', 'app', 'build', 'intermediates', 'sourcemaps', 'react', 'debug', 'index.android.bundle.packager.map');
const generatedBuildPath = _path().default.join(ctx.root, 'android', 'app', 'build', 'generated', 'sourcemaps', 'react', 'debug', 'index.android.bundle.map');
if (_fs().default.existsSync(generatedBuildPath)) {
_cliTools().logger.debug(`Getting the source map from ${generateSourcemap}`);
return generatedBuildPath;
} else if (_fs().default.existsSync(intermediateBuildPath)) {
_cliTools().logger.debug(`Getting the source map from ${intermediateBuildPath}`);
return intermediateBuildPath;
} else {
return generateSourcemap(port, bundleOptions);
}
}
//# sourceMappingURL=sourcemapUtils.ts.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,32 @@
{
"name": "@react-native-community/cli-hermes",
"version": "13.6.4",
"license": "MIT",
"main": "build/index.js",
"publishConfig": {
"access": "public"
},
"types": "build/index.d.ts",
"dependencies": {
"@react-native-community/cli-platform-android": "13.6.4",
"@react-native-community/cli-tools": "13.6.4",
"chalk": "^4.1.2",
"hermes-profile-transformer": "^0.0.6"
},
"files": [
"build",
"!*.d.ts",
"!*.map"
],
"devDependencies": {
"@react-native-community/cli-types": "13.6.4",
"@types/ip": "^1.1.0"
},
"homepage": "https://github.com/react-native-community/cli/tree/main/packages/cli-hermes",
"repository": {
"type": "git",
"url": "https://github.com/react-native-community/cli.git",
"directory": "packages/cli-hermes"
},
"gitHead": "a31b57ec24073420cef566c8bff2c6ae3e87c14f"
}