Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
{
"editor.rulers": [
80
],
"editor.rulers": [80],
Comment thread
jevakallio marked this conversation as resolved.
Outdated
"files.exclude": {
"**/.git": true,
"**/node_modules": true,
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import info from './info/info';
import config from './config/config';
import init from './init';
import doctor from './doctor';
import profile from './profile';

export const projectCommands = [
start,
Expand All @@ -24,6 +25,7 @@ export const projectCommands = [
info,
config,
doctor,
profile,
] as Command[];

export const detachedCommands = [init, doctor] as DetachedCommand[];
101 changes: 101 additions & 0 deletions packages/cli/src/commands/profile/downloadProfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import {Config} from '@react-native-community/cli-types';
import {execSync} from 'child_process';
import {logger, CLIError} from '@react-native-community/cli-tools';
import chalk from 'chalk';
import fs from 'fs';

/**
* get the last modified hermes profile
*/
function getLatestFile(packageName: string): string {
try {
const file = execSync(`adb shell run-as ${packageName} ls cache/ -tp | grep -v /$ | head -1
`);

return file.toString().trim();
} catch (e) {
throw new Error(e);
}
}
/**
* get the package name of the running React Native app
*/
function getPackageName(config: Config) {
const androidProject = config.project.android;

if (!androidProject) {
throw new CLIError(`
Android project not found. Are you sure this is a React Native project?
If your Android files are located in a non-standard location (e.g. not inside \'android\' folder), consider setting
\`project.android.sourceDir\` option to point to a new location.
`);
}
const {manifestPath} = androidProject;
const androidManifest = fs.readFileSync(manifestPath, 'utf8');

let packageNameMatchArray = androidManifest.match(/package="(.+?)"/);
if (!packageNameMatchArray || packageNameMatchArray.length === 0) {
throw new CLIError(
'Failed to build the app: No package name found. Found errors in /src/main/AndroidManifest.xml',
);
}

let packageName = packageNameMatchArray[1];

if (!validatePackageName(packageName)) {
logger.warn(
`Invalid application's package name "${chalk.bgRed(
packageName,
)}" in 'AndroidManifest.xml'. Read guidelines for setting the package name here: ${chalk.underline.dim(
'https://developer.android.com/studio/build/application-id',
)}`,
);
}
return packageName;
}
/** Validates that the package name is correct
*
*/

function validatePackageName(packageName: string) {
return /^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/.test(packageName);
}

/**
* Executes the commands to pull a hermes profile
* Commands:
* adb shell run-as com.rnhermesapp cp cache/sampling-profiler-trace1502707982002849976.cpuprofile /sdcard/latest.cpuprofile
* adb pull /sdcard/latest.cpuprofile
*/
export async function downloadProfile(
ctx: Config,
dstPath?: string,
fileName?: string,
) {
try {
const packageName = getPackageName(ctx);

const file = fileName || (await getLatestFile(packageName));
if (!file) {
logger.error(
'There is no file in the cache/ directory. Did you record a profile from the developer menu?',
);
process.exit(1);
}
logger.info(`File to be pulled: ${file}`);
execSync(`adb shell run-as ${packageName} cp cache/${file} /sdcard`);

//if not specify destination path, pull to the current directory
if (dstPath === undefined) {
execSync(`adb pull /sdcard/${file} ${ctx.root}`);
console.log(`Successfully pulled the file to ${ctx.root}/${file}`);
}
//if specified destination path, pull to that directory
else {
execSync(`adb pull /sdcard/${file} ${dstPath}`);
console.log(`Successfully pulled the file to ${dstPath}/${file}`);
}
} catch (e) {
throw new Error(e.message);
}
}
50 changes: 50 additions & 0 deletions packages/cli/src/commands/profile/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// @ts-ignore untyped
import {logger} from '@react-native-community/cli-tools';
import {Config} from '@react-native-community/cli-types';
import {downloadProfile} from './downloadProfile';

type Options = {
fileName?: string;
};

async function profile(
[dstPath]: Array<string>,
ctx: Config,
options: Options,
) {
try {
logger.info(
'Downloading a Hermes Sampling Profiler from your Android device...',
);

if (options.fileName) {
await downloadProfile(ctx, dstPath, options.fileName);
} else {
logger.info('No filename is provided, pulling latest file');
await downloadProfile(ctx, dstPath, undefined);
}
} catch (err) {
logger.error(`Unable to download the Hermes Sampling Profiler.\n${err}`);
}
}

export default {
name: 'profile-hermes [destinationDir]',
description:
'Download the Hermes Sampling Profiler to the directory <destinationDir> of the local machine',
func: profile,
options: [
//options: download the latest or fileName
{
name: '--fileName [string]',
description: 'Filename of the profile to be downloaded',
},
],
examples: [
{
desc:
'Download the Hermes Sampling Profiler to the directory <destinationDir> of the local machine',
cmd: 'profile-hermes /Users/phuonganh/Desktop',
},
],
};
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"compilerOptions": {
"target": "es2017",
"module": "commonjs",
"lib": ["es2017"],
"lib": ["es2017", "esnext.asynciterable"],
"declaration": true,
"declarationMap": true,
"composite": true,
Expand Down