Skip to content

Commit b31b62e

Browse files
committed
Scratch from get-appsize commands
1 parent 58beec3 commit b31b62e

File tree

11 files changed

+1400
-0
lines changed

11 files changed

+1400
-0
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules
2+
.DS_Store
3+
.vscode
4+
build/
5+
*.tsbuildinfo

.prettierrc.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
singleQuote: true,
3+
trailingComma: 'all',
4+
bracketSpacing: false,
5+
jsxBracketSameLine: true,
6+
};

package.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"name": "react-native-cli-plugin-benchmark",
3+
"version": "0.1.0",
4+
"description": "React Native CLI Plugin for Benchmark Regression",
5+
"scripts": {
6+
"build": "yarn tsc"
7+
},
8+
"main": "build/index.js",
9+
"files": [
10+
"build",
11+
"react-native.config.js"
12+
],
13+
"repository": "https://github.com/Kudo/react-native-cli-plugin-benchmark",
14+
"license": "MIT",
15+
"devDependencies": {
16+
"@react-native-community/cli-platform-android": "^4.0.1",
17+
"@react-native-community/cli-platform-ios": "^4.0.1",
18+
"@react-native-community/cli-types": "^3.0.0",
19+
"@types/glob": "^7.1.1",
20+
"@types/graceful-fs": "^4.1.3",
21+
"@types/lodash": "^4.14.149",
22+
"@types/node": "^13.7.1",
23+
"@types/node-fetch": "^2.5.4",
24+
"prettier": "^1.19.1",
25+
"typescript": "^3.7.5"
26+
},
27+
"peerDependencies": {
28+
"@react-native-community/cli": "^3.1.0"
29+
},
30+
"dependencies": {
31+
"execa": "^4.0.0",
32+
"glob": "^7.1.6",
33+
"graceful-fs": "^4.2.3",
34+
"lodash": "^4.17.15"
35+
}
36+
}

react-native.config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/**
2+
* @format
3+
*/
4+
5+
module.exports = require('./build').pluginConfig;

src/commands/getAppSizeAndroid.ts

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @format
8+
*/
9+
import glob from 'glob';
10+
import path from 'path';
11+
import execa from 'execa';
12+
import chalk from 'chalk';
13+
import fs from 'fs';
14+
import {Config} from '@react-native-community/cli-types';
15+
import {logger, CLIError} from '@react-native-community/cli-tools';
16+
import warnAboutManuallyLinkedLibs from '@react-native-community/cli-platform-android/build/link/warnAboutManuallyLinkedLibs';
17+
18+
// Verifies this is an Android project
19+
function checkAndroid(root: string) {
20+
return fs.existsSync(path.join(root, 'android/gradlew'));
21+
}
22+
23+
// Validates that the package name is correct
24+
function validatePackageName(packageName: string) {
25+
return /^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/.test(packageName);
26+
}
27+
28+
function performChecks(config: Config, args: Options) {
29+
if (!checkAndroid(args.root)) {
30+
throw new CLIError(
31+
'Android project not found. Are you sure this is a React Native project?',
32+
);
33+
}
34+
35+
// warn after we have done basic system checks
36+
warnAboutManuallyLinkedLibs(config);
37+
}
38+
39+
async function build(config: Config, args: Options, verbose: boolean = false) {
40+
performChecks(config, args);
41+
42+
if (args.jetifier) {
43+
logger.info(
44+
`Running ${chalk.bold(
45+
'jetifier',
46+
)} to migrate libraries to AndroidX. ${chalk.dim(
47+
'You can disable it using "--no-jetifier" flag.',
48+
)}`,
49+
);
50+
51+
try {
52+
await execa(require.resolve('jetifier/bin/jetify'), {
53+
stdio: verbose ? 'inherit' : 'ignore',
54+
});
55+
} catch (error) {
56+
throw new CLIError('Failed to run jetifier.', error);
57+
}
58+
}
59+
60+
process.chdir(path.join(args.root, 'android'));
61+
const cmd = process.platform.startsWith('win') ? 'gradlew.bat' : './gradlew';
62+
63+
// "app" is usually the default value for Android apps with only 1 app
64+
const {appFolder} = args;
65+
// @ts-ignore
66+
const androidManifest = fs.readFileSync(
67+
`${appFolder}/src/main/AndroidManifest.xml`,
68+
'utf8',
69+
);
70+
71+
let packageNameMatchArray = androidManifest.match(/package="(.+?)"/);
72+
if (!packageNameMatchArray || packageNameMatchArray.length === 0) {
73+
throw new CLIError(
74+
`Failed to build the app: No package name found. Found errors in ${chalk.underline.dim(
75+
`${appFolder}/src/main/AndroidManifest.xml`,
76+
)}`,
77+
);
78+
}
79+
80+
let packageName = packageNameMatchArray[1];
81+
82+
if (!validatePackageName(packageName)) {
83+
logger.warn(
84+
`Invalid application's package name "${chalk.bgRed(
85+
packageName,
86+
)}" in 'AndroidManifest.xml'. Read guidelines for setting the package name here: ${chalk.underline.dim(
87+
'https://developer.android.com/studio/build/application-id',
88+
)}`,
89+
); // we can also directly add the package naming rules here
90+
}
91+
92+
buildApk(cmd);
93+
}
94+
95+
function buildApk(
96+
gradlew: string,
97+
clean: boolean = true,
98+
verbose: boolean = false,
99+
) {
100+
try {
101+
// using '-x lint' in order to ignore linting errors while building the apk
102+
const gradleArgs = ['build', '-x', 'lint'];
103+
if (clean) gradleArgs.unshift('clean');
104+
logger.info('Building the app...');
105+
logger.debug(`Running command "${gradlew} ${gradleArgs.join(' ')}"`);
106+
execa.sync(gradlew, gradleArgs, {stdio: verbose ? 'inherit' : 'ignore'});
107+
} catch (error) {
108+
throw new CLIError('Failed to build the app.', error);
109+
}
110+
}
111+
112+
export interface Options {
113+
tasks?: Array<string>;
114+
root: string;
115+
variant: string;
116+
appFolder: string;
117+
jetifier: boolean;
118+
}
119+
120+
/**
121+
* Get generated APK size from as `run-android`
122+
*/
123+
async function getApkSize(_argv: Array<string>, config: Config, args: Options) {
124+
await build(config, args);
125+
126+
const {appFolder} = args;
127+
const variant = args.variant.toLowerCase();
128+
const buildDirectory = `${appFolder}/build/outputs/apk/${variant}`;
129+
const apks = glob.sync(path.join(buildDirectory, '**/*.apk'), {nodir: true});
130+
131+
type ApkWithSizeType = {[apk: string]: number};
132+
const apksWithSize = apks.reduce((map: ApkWithSizeType, apk) => {
133+
const {size} = fs.statSync(apk);
134+
const apkPath = path.resolve(apk);
135+
map[apkPath] = size;
136+
return map;
137+
}, {});
138+
logger.info(`Generated app size:\n${JSON.stringify(apksWithSize)}`);
139+
}
140+
141+
export default {
142+
name: 'get-appsize-android',
143+
description: 'get the generated APK size from run-android output',
144+
func: getApkSize,
145+
options: [
146+
{
147+
name: '--root [string]',
148+
description:
149+
'Override the root directory for the android build (which contains the android directory)',
150+
default: '',
151+
},
152+
{
153+
name: '--variant [string]',
154+
description: "Specify your app's build variant",
155+
default: 'debug',
156+
},
157+
{
158+
name: '--appFolder [string]',
159+
description:
160+
'Specify a different application folder name for the android source. If not, we assume is "app"',
161+
default: 'app',
162+
},
163+
{
164+
name: '--tasks [list]',
165+
description: 'Run custom Gradle tasks. By default it\'s "installDebug"',
166+
parse: (val: string) => val.split(','),
167+
},
168+
{
169+
name: '--no-jetifier',
170+
description:
171+
'Do not run "jetifier" – the AndroidX transition tool. By default it runs before Gradle to ease working with libraries that don\'t support AndroidX yet. See more at: https://www.npmjs.com/package/jetifier.',
172+
default: false,
173+
},
174+
{
175+
name: '--verbose',
176+
description: 'Show build log',
177+
default: false,
178+
},
179+
],
180+
};

0 commit comments

Comments
 (0)