Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/generate source map #6

Merged
merged 5 commits into from
Aug 11, 2020
Merged
Show file tree
Hide file tree
Changes from 4 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
6 changes: 2 additions & 4 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
{
"editor.rulers": [
80
],
"editor.rulers": [80],
"files.exclude": {
"**/.git": true,
"**/node_modules": true,
"**/node_modules": false,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing the settings.json changes weren't intentional?

"**/build": true
},
"editor.codeActionsOnSave": {
Expand Down
3 changes: 3 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
"@react-native-community/cli-server-api": "^4.10.1",
"@react-native-community/cli-tools": "^4.10.1",
"@react-native-community/cli-types": "^4.10.1",
"@types/ip": "^1.1.0",
"@types/source-map": "^0.5.7",
"axios": "^0.19.2",
"chalk": "^3.0.0",
"command-exists": "^1.2.8",
"commander": "^2.19.0",
Expand All @@ -46,6 +48,7 @@
"glob": "^7.1.3",
"graceful-fs": "^4.1.3",
"inquirer": "^3.0.6",
"ip": "^1.1.5",
"leven": "^3.1.0",
"lodash": "^4.17.15",
"metro": "^0.58.0",
Expand Down
5 changes: 4 additions & 1 deletion packages/cli/src/commands/bundle/buildBundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,13 @@ async function buildBundle(
ctx: Config,
output: typeof outputBundle = outputBundle,
) {
//console.log('not load metro config yet');
const config = await loadMetroConfig(ctx, {
maxWorkers: args.maxWorkers,
resetCache: args.resetCache,
config: args.config,
});
//console.log('loaded metro config');

if (config.resolver.platforms.indexOf(args.platform) === -1) {
logger.error(
Expand Down Expand Up @@ -84,8 +86,9 @@ async function buildBundle(
minify: args.minify !== undefined ? args.minify : !args.dev,
platform: args.platform,
};

//console.log('not build server yet');
const server = new Server(config);
//console.log('built new server');

try {
const bundle = await output.build(server, requestOpts);
Expand Down
3 changes: 3 additions & 0 deletions packages/cli/src/commands/bundle/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ function bundleWithOutput(
args: CommandLineArgs,
output: any, // untyped metro/src/shared/output/bundle or metro/src/shared/output/RamBundle
) {
// console.log('config: ', config);
// console.log('args: ', args);
// console.log('output: ', output);
return buildBundle(args, config, output);
}

Expand Down
112 changes: 108 additions & 4 deletions packages/cli/src/commands/profile/downloadProfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import fs from 'fs';
import path from 'path';
import os from 'os';
import {transformer} from './transformer';
import axios, {AxiosResponse} from 'axios';
import ip from 'ip';
import {SourceMap} from './EventInterfaces';

/**
* Get the last modified hermes profile
Expand Down Expand Up @@ -64,6 +67,83 @@ function validatePackageName(packageName: string) {
return /^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/.test(packageName);
}

async function getSourcemapFromServer(): Promise<AxiosResponse<SourceMap>> {
const DEBUG_SERVER_PORT = '8081';
const IP_ADDRESS = ip.address();
const PLATFORM = 'android';
const requestURL = `http://${IP_ADDRESS}:${DEBUG_SERVER_PORT}/index.map?platform=${PLATFORM}&dev=true`;
return (await axios.get(requestURL)) as AxiosResponse<SourceMap>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need axios for this. Taking the dependency for a single http request is most likely not going to be popular among the react-native-cli maintainers

}

async function getSourcemapPath(
ctx: Config,
generateSourceMap?: boolean,
): Promise<string> {
const osTmpDir = os.tmpdir();
//If '--generate-sourcemap, generate the bundle and source map files and store them in os.tmpDir()
if (generateSourceMap) {
console.log('generate new source map');
//Store the file in os.tmpDir()
const sourceMapPath = path.join(osTmpDir, 'index.map');

const sourceMapResult = await getSourcemapFromServer();
if (sourceMapResult) {
console.log('Request from server: done finding the source map');
fs.writeFileSync(
`${sourceMapPath}`,
JSON.stringify(sourceMapResult.data),
'utf-8',
);
console.log('Request from server: done writing the source map');
} else {
console.log('begin to build bundle');
execSync(
`react-native bundle --entry-file index.js --bundle-output ${path.join(
osTmpDir,
'index.bundle',
)} --sourcemap-output ${sourceMapPath}`,
);
}
logger.info(
`Successfully generated the source map and store it in ${sourceMapPath}`,
);
return `${sourceMapPath}`;
}

//Find the sourcemap if it exists
else {
console.log('find the existing source map');
//Find from local machine
//QUESTION: why is my source map path different from Jani's
//(android/app/build/generated/sourcemaps/react/debug/index.android.bundle.map)
const sourceMapDir = path.join(
ctx.root,
'android',
'app',
'build',
'intermediates', //'generated',
'sourcemaps',
'react',
'debug',
'index.android.bundle.packager.map',
);
console.log(sourceMapDir);
if (fs.existsSync(sourceMapDir)) {
console.log('get the sourcemap if it exists from local machine');
return sourceMapDir;
} else {
//Request from server
const sourceMapResult = await getSourcemapFromServer();
fs.writeFileSync(
`${path.join(osTmpDir, 'index.map')}`,
JSON.stringify(sourceMapResult.data),
'utf-8',
);
return `${path.join(osTmpDir, 'index.map')}`;
}
}
}

/**
* Executes the commands to pull a hermes profile
*/
Expand All @@ -73,8 +153,10 @@ export async function downloadProfile(
fileName?: string,
sourceMapPath?: string,
raw?: boolean,
generateSourceMap?: boolean,
) {
try {
//console.log('start the profile command');
const packageName = getPackageName(ctx);
//if not specify fileName, pull the latest file
const file = fileName || (await getLatestFile(packageName));
Expand All @@ -84,11 +166,12 @@ export async function downloadProfile(
);
process.exit(1);
}
logger.info(`File to be pulled: ${file}`);

//if not specify destination path, pull to the current directory
if (!dstPath) {
dstPath = ctx.root;
}
logger.info(`File to be pulled: ${file}`);
//if '--verbose' is enabled
if (logger.isVerbose()) {
logger.info('Internal commands run to pull the file: ');
Expand All @@ -103,13 +186,34 @@ export async function downloadProfile(
execSync(`adb pull /sdcard/${file} ${dstPath}`);
logger.success(`Successfully pulled the file to ${dstPath}/${file}`);
}

//Else: transform the profile to Chrome format and pull it to dstPath
else {
const tmpDir = path.join(os.tmpdir(), file);
execSync(`adb pull /sdcard/${file} ${tmpDir}`);
const osTmpDir = os.tmpdir();
const fileTmpDir = path.join(osTmpDir, file);
execSync(`adb pull /sdcard/${file} ${fileTmpDir}`);

//If path to source map is not given
if (!sourceMapPath) {
//Get or generate the source map
sourceMapPath = await getSourcemapPath(ctx, generateSourceMap);
//Run without source map
if (sourceMapPath && sourceMapPath.length === 0) {
logger.warn(
'Cannot generate or find bundle and source map, running the transformer without source map',
);
logger.info(
'Instructions on how to get source map:\n Go to directory android/app/build.gradle \n Set bundleInDebug: true',
);
}
}

//Run transformer tool to convert from Hermes to Chrome format
const events = await transformer(tmpDir, sourceMapPath, 'index.bundle');
const events = await transformer(
fileTmpDir,
sourceMapPath,
'index.bundle',
);
const transformedFilePath = `${dstPath}/${path.basename(
file,
'.cpuprofile',
Expand Down
17 changes: 12 additions & 5 deletions packages/cli/src/commands/profile/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ type Options = {
verbose: boolean;
fileName?: string;
raw?: boolean;
sourceMapPath?: string;
sourcemapPath?: string;
generateSourcemap?: boolean;
};

async function profile(
Expand All @@ -29,8 +30,9 @@ async function profile(
ctx,
dstPath,
options.fileName,
options.sourceMapPath,
options.sourcemapPath,
options.raw,
options.generateSourcemap,
);
} catch (err) {
logger.error(`Unable to download the Hermes Sampling Profiler.\n${err}`);
Expand All @@ -57,15 +59,20 @@ export default {
description: 'Pulling original Hermes formatted profile',
},
{
name: '--sourceMapPath [string]',
description: 'The local path to your source map file',
name: '--sourcemap-path [string]',
description:
'The local path to your source map file, eg. Users/.../Desktop/sourcemap.json',
},
{
name: '--generate-sourcemap',
description: 'Generate the JS bundle and source map',
},
],
examples: [
{
desc:
'Download the Hermes Sampling Profiler to the directory <destinationDir> of the local machine',
cmd: 'profile-hermes /Users/phuonganh/Desktop',
cmd: 'profile-hermes /Users/.../Desktop',
},
],
};
1 change: 1 addition & 0 deletions packages/cli/src/commands/profile/sourceMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const changeNamesToSourceMaps = async (
column: sm.column,
},
},
sm,
};
/**
* The name in source maps (for reasons I don't understand) is sometimes null, so OR-ing this
Expand Down
4 changes: 4 additions & 0 deletions packages/cli/src/commands/profile/transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ export const transformer = async (
sourceMapPath: string | undefined,
bundleFileName: string | undefined,
): Promise<DurationEvent[]> => {
//console.log('sourceMapPath: ', sourceMapPath);
//console.log('profile: ', profilePath);
//TODO: parse JSON file: check whether the file is empty
const hermesProfile = JSON.parse(fs.readFileSync(profilePath, 'utf-8'));
//console.log('hermes profile: ', hermesProfile.samples[0]);
const profileChunk = CpuProfilerModel.collectProfileEvents(hermesProfile);
const profiler = new CpuProfilerModel(profileChunk);
const chromeEvents = profiler.createStartEndEvents();
Expand Down
25 changes: 23 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2276,6 +2276,13 @@
dependencies:
"@types/hapi__joi" "*"

"@types/ip@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@types/ip/-/ip-1.1.0.tgz#aec4f5bfd49e4a4c53b590d88c36eb078827a7c0"
integrity sha512-dwNe8gOoF70VdL6WJBwVHtQmAX4RMd62M+mAB9HQFjG1/qiCLM/meRy95Pd14FYBbEDwCq7jgJs89cHpLBu4HQ==
dependencies:
"@types/node" "*"

"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
Expand Down Expand Up @@ -2889,6 +2896,13 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==

axios@^0.19.2:
version "0.19.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==
dependencies:
follow-redirects "1.5.10"

babel-eslint@10.0.1:
version "10.0.1"
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.1.tgz#919681dc099614cd7d31d45c8908695092a1faed"
Expand Down Expand Up @@ -4238,7 +4252,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
dependencies:
ms "2.0.0"

debug@3.1.0:
debug@3.1.0, debug@=3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
Expand Down Expand Up @@ -5312,6 +5326,13 @@ flush-write-stream@^1.0.0:
inherits "^2.0.3"
readable-stream "^2.3.6"

follow-redirects@1.5.10:
version "1.5.10"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==
dependencies:
debug "=3.1.0"

for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
Expand Down Expand Up @@ -6080,7 +6101,7 @@ ip-regex@^2.1.0:
resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9"
integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=

ip@1.1.5:
ip@1.1.5, ip@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=
Expand Down