@wavemaker-ai/wm-reactnative-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +236 -0
- package/assets/CLI-EnvironmentVariable.png +0 -0
- package/assets/EnvironmentVariable.png +0 -0
- package/assets/EnvironmentVariable1.png +0 -0
- package/files/ui-build.js +331 -0
- package/index.js +381 -0
- package/package.json +39 -0
- package/src/android.js +479 -0
- package/src/command.js +552 -0
- package/src/config.js +11 -0
- package/src/custom-logger/progress-bar.js +97 -0
- package/src/custom-logger/steps.js +117 -0
- package/src/custom-logger/task-logger.js +147 -0
- package/src/exec.js +73 -0
- package/src/expo-launcher.js +596 -0
- package/src/ios.js +517 -0
- package/src/logger.js +104 -0
- package/src/mobileprovision-parse/index.js +72 -0
- package/src/project-sync.service.js +390 -0
- package/src/requirements.js +250 -0
- package/src/utils.js +100 -0
- package/src/web-preview-launcher.js +548 -0
- package/src/zip.js +19 -0
- package/templates/embed/android/ReactNativeAppFragment.java +78 -0
- package/templates/embed/android/SplashScreenReactActivityLifecycleListener.kt +41 -0
- package/templates/embed/android/fragment_react_native_app.xml +14 -0
- package/templates/embed/ios/ReactNativeView.h +12 -0
- package/templates/embed/ios/ReactNativeView.m +59 -0
- package/templates/embed/ios/ReactNativeView.swift +53 -0
- package/templates/expo-camera-patch/useWebQRScanner.js +100 -0
- package/templates/ios-build-patch/podFIlePostInstall.js +72 -0
- package/templates/package/packageLock.json +14334 -0
- package/templates/wm-rn-runtime/App.js +479 -0
- package/templates/wm-rn-runtime/App.navigator.js +109 -0
- package/test.js +0 -0
- package/tools-site/index.html.template +17 -0
- package/tools-site/page_background.svg +99 -0
- package/tools-site/qrcode.js +614 -0
- package/tools-site/styles.css +39 -0
package/src/android.js
ADDED
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const propertiesReader = require('properties-reader');
|
|
3
|
+
const logger = require('./logger');
|
|
4
|
+
const config = require('./config');
|
|
5
|
+
const {
|
|
6
|
+
exec
|
|
7
|
+
} = require('./exec');
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
validateForAndroid,
|
|
11
|
+
checkForAndroidStudioAvailability
|
|
12
|
+
} = require('./requirements');
|
|
13
|
+
const { readAndReplaceFileContent } = require('./utils');
|
|
14
|
+
const taskLogger = require('./custom-logger/task-logger').spinnerBar;
|
|
15
|
+
const {androidBuildSteps} = require('./custom-logger/steps');
|
|
16
|
+
|
|
17
|
+
const loggerLabel = 'android-build';
|
|
18
|
+
|
|
19
|
+
function setKeyStoreValuesInGradleProps(content, keystoreName, ksData) {
|
|
20
|
+
// TODO: if key pwds are changed, then just update the values.
|
|
21
|
+
if(content.search(/MYAPP_UPLOAD_STORE_PASSWORD/gm) == -1) {
|
|
22
|
+
return content.concat(` \n MYAPP_UPLOAD_STORE_FILE=${keystoreName}
|
|
23
|
+
MYAPP_UPLOAD_KEY_ALIAS=${ksData.keyAlias}
|
|
24
|
+
MYAPP_UPLOAD_STORE_PASSWORD=${ksData.storePassword}
|
|
25
|
+
MYAPP_UPLOAD_KEY_PASSWORD=${ksData.keyPassword}`);
|
|
26
|
+
}
|
|
27
|
+
return content;
|
|
28
|
+
}
|
|
29
|
+
// Reference: http://reactnative.dev/docs/signed-apk-android
|
|
30
|
+
async function generateSignedApk(keyStore, storePassword, keyAlias, keyPassword, packageType) {
|
|
31
|
+
const ksData = {storePassword: storePassword, keyAlias: keyAlias, keyPassword: keyPassword};
|
|
32
|
+
const namesArr = keyStore.split('/');
|
|
33
|
+
const keystoreName = namesArr[namesArr.length - 1];
|
|
34
|
+
const filepath = config.src + 'android/app/' + keystoreName;
|
|
35
|
+
|
|
36
|
+
fs.copyFileSync(keyStore, filepath);
|
|
37
|
+
|
|
38
|
+
// edit file android/gradle.properties
|
|
39
|
+
const gradlePropsPath = config.src + 'android/gradle.properties';
|
|
40
|
+
if (fs.existsSync(gradlePropsPath)) {
|
|
41
|
+
let data = fs.readFileSync(gradlePropsPath, 'utf8');
|
|
42
|
+
let content = await setKeyStoreValuesInGradleProps(data, keystoreName, ksData);
|
|
43
|
+
fs.writeFileSync(gradlePropsPath, content);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const appGradlePath = config.src + 'android/app/build.gradle';
|
|
47
|
+
let content = fs.readFileSync(appGradlePath, 'utf8');
|
|
48
|
+
content = await updateSigningConfig(content);
|
|
49
|
+
fs.writeFileSync(appGradlePath, content);
|
|
50
|
+
await generateAab(packageType);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function updateSigningConfig(content) {
|
|
54
|
+
// TODO: replace one of the buildTypes to signingConfigs.release
|
|
55
|
+
if(content.search(/if \(project.hasProperty\(\'MYAPP_UPLOAD_STORE_FILE\'\)\)/gm) == -1) {
|
|
56
|
+
content = content.replace(/signingConfigs\.debug/g, 'signingConfigs.release');
|
|
57
|
+
return content.replace(/signingConfigs \{/gm, `signingConfigs {
|
|
58
|
+
release {
|
|
59
|
+
if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
|
|
60
|
+
storeFile file(MYAPP_UPLOAD_STORE_FILE)
|
|
61
|
+
storePassword MYAPP_UPLOAD_STORE_PASSWORD
|
|
62
|
+
keyAlias MYAPP_UPLOAD_KEY_ALIAS
|
|
63
|
+
keyPassword MYAPP_UPLOAD_KEY_PASSWORD
|
|
64
|
+
}
|
|
65
|
+
}`);
|
|
66
|
+
}
|
|
67
|
+
return content;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function updateJSEnginePreference() {
|
|
71
|
+
const jsEngine = require(config.src + 'app.json').expo.jsEngine;
|
|
72
|
+
const gradlePropsPath = config.src + 'android/gradle.properties';
|
|
73
|
+
if (fs.existsSync(gradlePropsPath)) {
|
|
74
|
+
let data = fs.readFileSync(gradlePropsPath, 'utf8');
|
|
75
|
+
data = data.replace(/expo\.jsEngine=(jsc|hermes)/, `expo.jsEngine=${jsEngine}`)
|
|
76
|
+
fs.writeFileSync(gradlePropsPath, data);
|
|
77
|
+
logger.info({
|
|
78
|
+
label: loggerLabel,
|
|
79
|
+
message: `js engine is set as ${jsEngine}`
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function setSigningConfigInGradle() {
|
|
85
|
+
const gradlePath = config.src + 'android/app/build.gradle';
|
|
86
|
+
|
|
87
|
+
let content = fs.readFileSync(gradlePath, 'utf8');
|
|
88
|
+
content = updateSigningConfig(content);
|
|
89
|
+
fs.writeFileSync(gradlePath, content);
|
|
90
|
+
|
|
91
|
+
generateAab();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function addKeepFileEntries() {
|
|
95
|
+
fs.mkdirSync(config.src + 'android/app/src/main/res/raw/', {recursive: true});
|
|
96
|
+
const data = `<?xml version="1.0" encoding="utf-8"?>
|
|
97
|
+
<resources xmlns:tools="http://schemas.android.com/tools"
|
|
98
|
+
tools:keep="@raw/*_fontawesome,@raw/*__streamlinelighticon, @raw/*_wavicon, @raw/*_streamlineregularicon" />`;
|
|
99
|
+
fs.appendFileSync(config.src + 'android/app/src/main/res/raw/keep.xml', data);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
async function generateAab(packageType) {
|
|
104
|
+
try {
|
|
105
|
+
// addKeepFileEntries();
|
|
106
|
+
await exec('./gradlew', ['clean'], {
|
|
107
|
+
cwd: config.src + 'android'
|
|
108
|
+
});
|
|
109
|
+
logger.info('****** invoking aab build *****');
|
|
110
|
+
if (packageType === 'bundle') {
|
|
111
|
+
await exec('./gradlew', [':app:bundleRelease'], {
|
|
112
|
+
cwd: config.src + 'android'
|
|
113
|
+
});
|
|
114
|
+
} else {
|
|
115
|
+
await exec('./gradlew', ['assembleRelease'], {
|
|
116
|
+
cwd: config.src + 'android'
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch(e) {
|
|
121
|
+
console.error('error generating release apk. ', e);
|
|
122
|
+
return {
|
|
123
|
+
success: false,
|
|
124
|
+
errors: e
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const endWith = (str, suffix) => {
|
|
130
|
+
if (!str.endsWith(suffix)) {
|
|
131
|
+
return str += suffix;
|
|
132
|
+
}
|
|
133
|
+
return str;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
function findFile(path, nameregex) {
|
|
137
|
+
const files = fs.readdirSync(path);
|
|
138
|
+
const f = files.find(f => f.match(nameregex));
|
|
139
|
+
return endWith(path, '/') + f;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function addProguardRule() {
|
|
143
|
+
const proguardRulePath = config.src + 'android/app/proguard-rules.pro';
|
|
144
|
+
if (fs.existsSync(proguardRulePath)) {
|
|
145
|
+
var data = `-keep class com.facebook.react.turbomodule.** { *; }`;
|
|
146
|
+
fs.appendFileSync(proguardRulePath,data, 'utf8');
|
|
147
|
+
logger.info('***** added proguard rule ******')
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function updateOptimizationFlags() {
|
|
152
|
+
logger.info('***** into optimization ******')
|
|
153
|
+
const buildGradlePath = config.src + 'android/app/build.gradle';
|
|
154
|
+
if (fs.existsSync(buildGradlePath)) {
|
|
155
|
+
let content = fs.readFileSync(buildGradlePath, 'utf8');
|
|
156
|
+
if (content.search(`def enableProguardInReleaseBuilds = false`) > -1) {
|
|
157
|
+
content = content.replace(/def enableProguardInReleaseBuilds = false/gm, `def enableProguardInReleaseBuilds = true`)
|
|
158
|
+
.replace(/minifyEnabled enableProguardInReleaseBuilds/gm, `minifyEnabled enableProguardInReleaseBuilds\n shrinkResources false\n`);
|
|
159
|
+
}
|
|
160
|
+
content = content.replace(
|
|
161
|
+
/shrinkResources\s*\(\sfindProperty\('android\.enableShrinkResourcesInReleaseBuilds'\)\?.?:\sfalse\s\)/g,
|
|
162
|
+
"shrinkResources true"
|
|
163
|
+
)
|
|
164
|
+
.replace(
|
|
165
|
+
/minifyEnabled\s+\(?\s*(enableProguardInReleaseBuilds)\s*\)?/g,
|
|
166
|
+
"minifyEnabled true\n " // Adds a proper newline & indentation
|
|
167
|
+
);
|
|
168
|
+
fs.writeFileSync(buildGradlePath, content);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async function updateAndroidBuildGradleFile(type) {
|
|
173
|
+
const buildGradlePath = config.src + 'android/app/build.gradle';
|
|
174
|
+
if (fs.existsSync(buildGradlePath)) {
|
|
175
|
+
let content = fs.readFileSync(buildGradlePath, 'utf8');
|
|
176
|
+
if (type === 'release') {
|
|
177
|
+
if (content.search(`entryFile: "index.js"`) === -1) {
|
|
178
|
+
content = content.replace(/^(?!\s)project\.ext\.react = \[/gm, `project.ext.react = [
|
|
179
|
+
entryFile: "index.js",
|
|
180
|
+
bundleAssetName: "index.android.bundle",
|
|
181
|
+
bundleInRelease: true,`);
|
|
182
|
+
} else {
|
|
183
|
+
content = content.replace(/bundleInDebug\: true/gm, `bundleInDebug: false,
|
|
184
|
+
bundleInRelease: true,`).replace(/devDisabledInDebug\: true/gm, ``)
|
|
185
|
+
.replace(/bundleInRelease\: false/gm, `bundleInRelease: true`);
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
if (content.search(`entryFile: "index.js"`) === -1 && content.search('project.ext.react =') >= 0) {
|
|
189
|
+
content = content.replace(/^(?!\s)project\.ext\.react = \[/gm, `project.ext.react = [
|
|
190
|
+
entryFile: "index.js",
|
|
191
|
+
bundleAssetName: "index.android.bundle",
|
|
192
|
+
bundleInDebug: true,
|
|
193
|
+
devDisabledInDebug: true,`);
|
|
194
|
+
} else if (content.indexOf(`bundleInDebug:`) >= 0) {
|
|
195
|
+
content = content.replace(/bundleInDebug\: false/gm, `bundleInDebug: true`)
|
|
196
|
+
.replace(/devDisabledInDebug\: false/gm, `devDisabledInDebug: true`)
|
|
197
|
+
.replace(/bundleInRelease\: true/gm, `bundleInRelease: false`);
|
|
198
|
+
} else {
|
|
199
|
+
await createJSBundle();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
fs.writeFileSync(buildGradlePath, content);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function updateSettingsGradleFile(appName) {
|
|
207
|
+
const path = config.src + 'android/settings.gradle';
|
|
208
|
+
let content = fs.readFileSync(path, 'utf8');
|
|
209
|
+
if (content.search(/^rootProject.name = \'\'/gm) > -1) {
|
|
210
|
+
content = content.replace(/^rootProject.name = \'\'/gm, `rootProject.name = ${appName}`);
|
|
211
|
+
fs.writeFileSync(path, content);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function createJSBundle() {
|
|
216
|
+
fs.mkdirpSync(config.src + '/android/app/src/main/assets');
|
|
217
|
+
return await exec('npx', ['expo', 'export:embed', '--platform', 'android',
|
|
218
|
+
'--dev', 'false', '--entry-file', 'index.js',
|
|
219
|
+
'--bundle-output', 'android/app/src/main/assets/index.android.bundle',
|
|
220
|
+
'--assets-dest', 'android/app/src/main/res/', '--reset-cache'], {
|
|
221
|
+
cwd: config.src
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async function embed(args) {
|
|
226
|
+
const rnAndroidProject = `${config.src}/android`;
|
|
227
|
+
const embedAndroidProject = `${config.src}/android-embed`;
|
|
228
|
+
fs.mkdirpSync(embedAndroidProject);
|
|
229
|
+
logger.info({
|
|
230
|
+
label: loggerLabel,
|
|
231
|
+
message: 'copying Native Android project.'
|
|
232
|
+
});
|
|
233
|
+
fs.copySync(args.modulePath, embedAndroidProject);
|
|
234
|
+
fs.copySync(
|
|
235
|
+
`${__dirname}/../templates/embed/android/fragment_react_native_app.xml`,
|
|
236
|
+
`${embedAndroidProject}/rnApp/src/main/res/layout/fragment_react_native_app.xml`);
|
|
237
|
+
fs.copySync(
|
|
238
|
+
`${__dirname}/../templates/embed/android/ReactNativeAppFragment.java`,
|
|
239
|
+
`${embedAndroidProject}/app/src/main/java/com/wavemaker/reactnative/ReactNativeAppFragment.java`);
|
|
240
|
+
await readAndReplaceFileContent(
|
|
241
|
+
`${embedAndroidProject}/app/src/main/java/com/wavemaker/reactnative/ReactNativeAppFragment.java`,
|
|
242
|
+
content => content.replace(/\$\{packageName\}/g, config.metaData.id));
|
|
243
|
+
logger.info({
|
|
244
|
+
label: loggerLabel,
|
|
245
|
+
message: 'transforming Native Android files.'
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// NATIVE CHANGES
|
|
249
|
+
|
|
250
|
+
// settings.gradle changes
|
|
251
|
+
await readAndReplaceFileContent(`${embedAndroidProject}/settings.gradle`, (content) => {
|
|
252
|
+
content = content.replace(`dependencyResolutionManagement {
|
|
253
|
+
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
|
254
|
+
repositories {
|
|
255
|
+
google()
|
|
256
|
+
mavenCentral()
|
|
257
|
+
}
|
|
258
|
+
}`, ``);
|
|
259
|
+
content = content.replace(`include ':app'`, `include ':app'\napply from:'./rnApp/root.settings.gradle'`);
|
|
260
|
+
return content;
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// gradle.properties changes
|
|
264
|
+
await readAndReplaceFileContent(
|
|
265
|
+
`${embedAndroidProject}/gradle.properties`,
|
|
266
|
+
(content) => {
|
|
267
|
+
const nativeProperties = propertiesReader(`${embedAndroidProject}/gradle.properties`);
|
|
268
|
+
const rnProperties = propertiesReader(`${rnAndroidProject}/gradle.properties`);
|
|
269
|
+
content += (Object.keys(rnProperties.getAllProperties())
|
|
270
|
+
.filter(k => (nativeProperties.get(k) === null))
|
|
271
|
+
.map(k => `\n${k}=${rnProperties.get(k)}`)).join('') || '';
|
|
272
|
+
content = content.replace('android.nonTransitiveRClass=true', 'android.nonTransitiveRClass=false');
|
|
273
|
+
return content.replace(`org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8`, `org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m`);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// build.gradle changes
|
|
277
|
+
await readAndReplaceFileContent(`${embedAndroidProject}/build.gradle`, (content) => {
|
|
278
|
+
return content + `apply from:'./rnApp/root.build.gradle'`;
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// app/build.gradle changes
|
|
282
|
+
await readAndReplaceFileContent(`${embedAndroidProject}/app/build.gradle`, (content) => {
|
|
283
|
+
content = content.replace(`plugins {`, `plugins {\n\tid "com.facebook.react"`);
|
|
284
|
+
content = content.replace(`dependencies {`, `dependencies {\n\timplementation project(':rnApp')\n\timplementation 'com.facebook.react:react-native:+'\n`);
|
|
285
|
+
return content;
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
logger.info({
|
|
289
|
+
label: loggerLabel,
|
|
290
|
+
message: 'Changed Native Android project.'
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// REACT NATIVE CHANGES
|
|
294
|
+
|
|
295
|
+
fs.copySync(`${rnAndroidProject}/app`, `${embedAndroidProject}/rnApp`);
|
|
296
|
+
fs.copySync(`${rnAndroidProject}/build.gradle`, `${embedAndroidProject}/rnApp/root.build.gradle`);
|
|
297
|
+
fs.copySync(`${rnAndroidProject}/settings.gradle`, `${embedAndroidProject}/rnApp/root.settings.gradle`);
|
|
298
|
+
|
|
299
|
+
// rnApp/root.build.gradle changes
|
|
300
|
+
await readAndReplaceFileContent(`${embedAndroidProject}/rnApp/root.build.gradle`, (content) => {
|
|
301
|
+
return content + `\nallprojects {
|
|
302
|
+
configurations.all {
|
|
303
|
+
resolutionStrategy {
|
|
304
|
+
force "com.facebook.react:react-native:+"
|
|
305
|
+
force "com.facebook.react:hermes-android:+"
|
|
306
|
+
force "androidx.annotation:annotation:1.4.0"
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}`;
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// rnApp/root.settings.gradle changes
|
|
313
|
+
await readAndReplaceFileContent(`${embedAndroidProject}/rnApp/root.settings.gradle`, (content) => {
|
|
314
|
+
content = content.replace('rootProject.name', '//rootProject.name');
|
|
315
|
+
return content.replace(`':app'`, `':rnApp'`);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// rnApp/src/main/AndroidManifest changes
|
|
319
|
+
await readAndReplaceFileContent(
|
|
320
|
+
`${embedAndroidProject}/rnApp/src/main/AndroidManifest.xml`,
|
|
321
|
+
(markup) => markup.replace(
|
|
322
|
+
/<intent-filter>(.|\n)*?android:name="android.intent.category.LAUNCHER"(.|\n)*?<\/intent-filter>/g,
|
|
323
|
+
'<!-- Removed React Native Main activity as launcher. Check the embedApp with Launcher activity -->')
|
|
324
|
+
.replace(' android:theme="@style/AppTheme"', ''));
|
|
325
|
+
|
|
326
|
+
// rnApp/build.gradle changes
|
|
327
|
+
await readAndReplaceFileContent(
|
|
328
|
+
`${embedAndroidProject}/rnApp/build.gradle`,
|
|
329
|
+
(content) => {
|
|
330
|
+
return content.replace(
|
|
331
|
+
`apply plugin: "com.android.application"`,
|
|
332
|
+
`apply plugin: "com.android.library"`)
|
|
333
|
+
.replace(/\s*applicationId.*/, '')
|
|
334
|
+
.replace(`"/scripts/compose-source-maps.js",`,
|
|
335
|
+
`"/scripts/compose-source-maps.js",\n\tenableVmCleanup: false`)
|
|
336
|
+
.replace('applicationVariants.all { variant', '/*applicationVariants.all { variant')
|
|
337
|
+
.replace('implementation "com.facebook.react:react-native:+"', 'api "com.facebook.react:react-native:+"')
|
|
338
|
+
.replace(
|
|
339
|
+
/(versionCodes.get\(abi\)\s\*\s1048576\s\+\sdefaultConfig\.versionCode[\s|\n]*\}[\s|\n]*\}[\s|\n]*\})/,
|
|
340
|
+
'$1*/'
|
|
341
|
+
);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
fs.copySync(
|
|
345
|
+
`${__dirname}/../templates/embed/android/SplashScreenReactActivityLifecycleListener.kt`,
|
|
346
|
+
`${config.src}/node_modules/expo-splash-screen/android/src/main/java/expo/modules/splashscreen/SplashScreenReactActivityLifecycleListener.kt`);
|
|
347
|
+
await readAndReplaceFileContent(
|
|
348
|
+
`${args.dest}/app.js`,
|
|
349
|
+
(content) => content.replace('props = props || {};', 'props = props || {};\n\tprops.landingPage = props.landingPage || props.pageName;'));
|
|
350
|
+
fs.mkdirpSync(`${config.src}/android-embed/rnApp/src/main/assets`);
|
|
351
|
+
const nodeModules = `${args.dest}/node_modules`;
|
|
352
|
+
const wmScope = fs.existsSync(`${nodeModules}/@wavemaker`)
|
|
353
|
+
? '@wavemaker'
|
|
354
|
+
: fs.existsSync(`${nodeModules}/@wavemaker-ai`)
|
|
355
|
+
? '@wavemaker-ai'
|
|
356
|
+
: null;
|
|
357
|
+
if (wmScope) {
|
|
358
|
+
const dialogContentPath = `${nodeModules}/${wmScope}/app-rn-runtime/components/dialogs/dialogcontent/dialogcontent.component.js`;
|
|
359
|
+
if (fs.existsSync(dialogContentPath)) {
|
|
360
|
+
await readAndReplaceFileContent(
|
|
361
|
+
dialogContentPath,
|
|
362
|
+
(content) => content.replace('height', 'maxHeight'));
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
await exec('npx', ['expo', 'export:embed', '--platform', 'android',
|
|
366
|
+
'--dev', 'false', '--entry-file', 'index.js',
|
|
367
|
+
'--bundle-output', 'android-embed/rnApp/src/main/assets/index.android.bundle',
|
|
368
|
+
'--assets-dest', 'android-embed/rnApp/src/main/res/', '--reset-cache'], {
|
|
369
|
+
cwd: config.src
|
|
370
|
+
});
|
|
371
|
+
logger.info({
|
|
372
|
+
label: loggerLabel,
|
|
373
|
+
message: 'Changed React Native project.'
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async function invokeAndroidBuild(args) {
|
|
378
|
+
taskLogger.start(androidBuildSteps[4].start);
|
|
379
|
+
taskLogger.setTotal(androidBuildSteps[4].total);
|
|
380
|
+
let keyStore, storePassword, keyAlias,keyPassword;
|
|
381
|
+
|
|
382
|
+
if (args.buildType === 'debug' && !args.aKeyStore) {
|
|
383
|
+
keyStore = __dirname + '/../defaults/android-debug.keystore';
|
|
384
|
+
keyAlias = 'androiddebugkey';
|
|
385
|
+
keyPassword = 'android';
|
|
386
|
+
storePassword = 'android';
|
|
387
|
+
} else {
|
|
388
|
+
keyStore = args.aKeyStore,
|
|
389
|
+
storePassword = args.aStorePassword,
|
|
390
|
+
keyAlias = args.aKeyAlias,
|
|
391
|
+
keyPassword = args.aKeyPassword
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (!await checkForAndroidStudioAvailability()) {
|
|
395
|
+
return {
|
|
396
|
+
success: false
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
await readAndReplaceFileContent(
|
|
400
|
+
`${args.dest}/App.js`,
|
|
401
|
+
(content) => {
|
|
402
|
+
return content + `
|
|
403
|
+
// Remove cookies with no expiry time set
|
|
404
|
+
(function() {
|
|
405
|
+
try {
|
|
406
|
+
require('@react-native-cookies/cookies').removeSessionCookies();
|
|
407
|
+
} catch(e) {
|
|
408
|
+
console.error(e);
|
|
409
|
+
}
|
|
410
|
+
}());
|
|
411
|
+
`
|
|
412
|
+
});
|
|
413
|
+
updateJSEnginePreference();
|
|
414
|
+
const appName = config.metaData.name;
|
|
415
|
+
updateSettingsGradleFile(appName);
|
|
416
|
+
if (args.buildType === 'release') {
|
|
417
|
+
const errors = validateForAndroid(keyStore, storePassword, keyAlias, keyPassword);
|
|
418
|
+
if (errors.length > 0) {
|
|
419
|
+
return {
|
|
420
|
+
success: false,
|
|
421
|
+
errors: errors
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
addProguardRule();
|
|
425
|
+
updateOptimizationFlags();
|
|
426
|
+
updateAndroidBuildGradleFile(args.buildType);
|
|
427
|
+
taskLogger.incrementProgress(1);
|
|
428
|
+
await generateSignedApk(keyStore, storePassword, keyAlias, keyPassword, args.packageType);
|
|
429
|
+
taskLogger.succeed(androidBuildSteps[4].succeed);
|
|
430
|
+
} else {
|
|
431
|
+
await updateAndroidBuildGradleFile(args.buildType);
|
|
432
|
+
logger.info({
|
|
433
|
+
label: loggerLabel,
|
|
434
|
+
message: 'Updated build.gradle file with debug configuration'
|
|
435
|
+
});
|
|
436
|
+
taskLogger.incrementProgress(0.5)
|
|
437
|
+
try {
|
|
438
|
+
await exec('./gradlew', ['assembleDebug'], {
|
|
439
|
+
cwd: config.src + 'android'
|
|
440
|
+
});
|
|
441
|
+
taskLogger.incrementProgress(1.2)
|
|
442
|
+
taskLogger.succeed(androidBuildSteps[4].succeed);
|
|
443
|
+
} catch(e) {
|
|
444
|
+
console.error('error generating release apk. ', e);
|
|
445
|
+
taskLogger.fail(androidBuildSteps[4].fail);
|
|
446
|
+
return {
|
|
447
|
+
success: false,
|
|
448
|
+
errors: e
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
logger.info({
|
|
453
|
+
label: loggerLabel,
|
|
454
|
+
message: 'build completed'
|
|
455
|
+
});
|
|
456
|
+
taskLogger.succeed('build completed')
|
|
457
|
+
const output = args.dest + 'output/android/';
|
|
458
|
+
const outputFilePath = `${output}${appName}(${config.metaData.version}).${args.buildType}.${args.packageType === 'bundle' ? 'aab': 'apk'}`;
|
|
459
|
+
|
|
460
|
+
let bundlePath = null;
|
|
461
|
+
let folder = args.buildType === 'release' ? 'release' : 'debug';
|
|
462
|
+
if (args.packageType === 'bundle') {
|
|
463
|
+
bundlePath = findFile(`${args.dest}android/app/build/outputs/bundle/${folder}`, /\.aab?/);
|
|
464
|
+
} else {
|
|
465
|
+
bundlePath = findFile(`${args.dest}android/app/build/outputs/apk/${folder}`, /\.apk?/);
|
|
466
|
+
}
|
|
467
|
+
fs.mkdirSync(output, {recursive: true});
|
|
468
|
+
fs.copyFileSync(bundlePath, outputFilePath);
|
|
469
|
+
return {
|
|
470
|
+
success: true,
|
|
471
|
+
output: outputFilePath
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
module.exports = {
|
|
476
|
+
generateSignedApk: generateSignedApk,
|
|
477
|
+
invokeAndroidBuild: invokeAndroidBuild,
|
|
478
|
+
embed: embed
|
|
479
|
+
}
|