@revopush/code-push-cli 0.0.5 → 0.0.7
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/bin/script/command-executor.js +44 -32
- package/bin/script/react-native-utils.js +188 -16
- package/package.json +2 -1
- package/script/command-executor.ts +69 -46
- package/script/react-native-utils.ts +206 -20
- package/test.json +5963 -0
- package/test2.json +5844 -0
|
@@ -1,35 +1,17 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
3
|
|
|
4
|
-
import AccountManager = require("./management-sdk");
|
|
5
|
-
|
|
6
4
|
const childProcess = require("child_process");
|
|
7
5
|
import debugCommand from "./commands/debug";
|
|
8
6
|
import * as fs from "fs";
|
|
9
7
|
import * as chalk from "chalk";
|
|
10
|
-
|
|
11
|
-
const g2js = require("gradle-to-js/lib/parser");
|
|
12
8
|
import * as moment from "moment";
|
|
13
|
-
|
|
14
|
-
const opener = require("opener");
|
|
15
9
|
import * as os from "os";
|
|
16
10
|
import * as path from "path";
|
|
17
|
-
|
|
18
|
-
const plist = require("plist");
|
|
19
|
-
const progress = require("progress");
|
|
20
|
-
const prompt = require("prompt");
|
|
21
11
|
import * as Q from "q";
|
|
22
|
-
|
|
23
|
-
const rimraf = require("rimraf");
|
|
24
12
|
import * as semver from "semver";
|
|
25
|
-
|
|
26
|
-
const Table = require("cli-table");
|
|
27
|
-
const which = require("which");
|
|
28
|
-
import wordwrap = require("wordwrap");
|
|
29
13
|
import * as cli from "../script/types/cli";
|
|
30
14
|
import sign from "./sign";
|
|
31
|
-
|
|
32
|
-
const xcode = require("xcode");
|
|
33
15
|
import {
|
|
34
16
|
AccessKey,
|
|
35
17
|
Account,
|
|
@@ -45,14 +27,38 @@ import {
|
|
|
45
27
|
Session,
|
|
46
28
|
UpdateMetrics,
|
|
47
29
|
} from "../script/types";
|
|
48
|
-
import {
|
|
49
|
-
|
|
30
|
+
import {
|
|
31
|
+
getBundleSourceMapOutput,
|
|
32
|
+
getMinifyParams,
|
|
33
|
+
getReactNativePackagePath,
|
|
34
|
+
isHermesEnabled,
|
|
35
|
+
isValidVersion,
|
|
36
|
+
runHermesEmitBinaryCommand,
|
|
37
|
+
} from "./react-native-utils";
|
|
38
|
+
import { fileDoesNotExistOrIsDirectory, fileExists, isBinaryOrZip } from "./utils/file-utils";
|
|
39
|
+
|
|
40
|
+
import AccountManager = require("./management-sdk");
|
|
41
|
+
import wordwrap = require("wordwrap");
|
|
42
|
+
import Promise = Q.Promise;
|
|
43
|
+
|
|
44
|
+
const g2js = require("gradle-to-js/lib/parser");
|
|
45
|
+
|
|
46
|
+
const opener = require("opener");
|
|
47
|
+
|
|
48
|
+
const plist = require("plist");
|
|
49
|
+
const progress = require("progress");
|
|
50
|
+
const prompt = require("prompt");
|
|
51
|
+
|
|
52
|
+
const rimraf = require("rimraf");
|
|
53
|
+
|
|
54
|
+
const Table = require("cli-table");
|
|
55
|
+
|
|
56
|
+
const xcode = require("xcode");
|
|
50
57
|
|
|
51
58
|
const configFilePath: string = path.join(process.env.LOCALAPPDATA || process.env.HOME, ".revopush.config");
|
|
52
59
|
const emailValidator = require("email-validator");
|
|
53
60
|
const packageJson = require("../../package.json");
|
|
54
61
|
const parseXml = Q.denodeify(require("xml2js").parseString);
|
|
55
|
-
import Promise = Q.Promise;
|
|
56
62
|
|
|
57
63
|
const properties = require("properties");
|
|
58
64
|
|
|
@@ -1259,6 +1265,7 @@ export const releaseReact = (command: cli.IReleaseReactCommand): Promise<void> =
|
|
|
1259
1265
|
let bundleName: string = command.bundleName;
|
|
1260
1266
|
let entryFile: string = command.entryFile;
|
|
1261
1267
|
const outputFolder: string = command.outputDir || path.join(os.tmpdir(), "CodePush");
|
|
1268
|
+
const sourcemapOutputFolder: string = command.sourcemapOutput || path.join(os.tmpdir(), "CodePushSourceMap");
|
|
1262
1269
|
const platform: string = (command.platform = command.platform.toLowerCase());
|
|
1263
1270
|
const releaseCommand: cli.IReleaseCommand = <any>command;
|
|
1264
1271
|
// Check for app and deployment exist before releasing an update.
|
|
@@ -1266,7 +1273,7 @@ export const releaseReact = (command: cli.IReleaseReactCommand): Promise<void> =
|
|
|
1266
1273
|
return (
|
|
1267
1274
|
sdk
|
|
1268
1275
|
.getDeployment(command.appName, command.deploymentName)
|
|
1269
|
-
.then(()
|
|
1276
|
+
.then(async () => {
|
|
1270
1277
|
releaseCommand.package = outputFolder;
|
|
1271
1278
|
|
|
1272
1279
|
switch (platform) {
|
|
@@ -1319,8 +1326,9 @@ export const releaseReact = (command: cli.IReleaseReactCommand): Promise<void> =
|
|
|
1319
1326
|
? Q(command.appStoreVersion)
|
|
1320
1327
|
: getReactNativeProjectAppVersion(command, projectName);
|
|
1321
1328
|
|
|
1322
|
-
if (
|
|
1323
|
-
|
|
1329
|
+
if (!sourcemapOutputFolder.endsWith(".map") && !command.sourcemapOutput) {
|
|
1330
|
+
// create tmp dir only if no dir was given by user. User must crete a directory if --sourcemapOutput is passes
|
|
1331
|
+
await createEmptyTempReleaseFolder(sourcemapOutputFolder);
|
|
1324
1332
|
}
|
|
1325
1333
|
|
|
1326
1334
|
return appVersionPromise;
|
|
@@ -1334,29 +1342,28 @@ export const releaseReact = (command: cli.IReleaseReactCommand): Promise<void> =
|
|
|
1334
1342
|
// This is needed to clear the react native bundler cache:
|
|
1335
1343
|
// https://github.com/facebook/react-native/issues/4289
|
|
1336
1344
|
.then(() => deleteFolder(`${os.tmpdir()}/react-*`))
|
|
1337
|
-
.then(() =>
|
|
1338
|
-
runReactNativeBundleCommand(
|
|
1345
|
+
.then(async () => {
|
|
1346
|
+
await runReactNativeBundleCommand(
|
|
1347
|
+
command,
|
|
1339
1348
|
bundleName,
|
|
1340
1349
|
command.development || false,
|
|
1341
1350
|
entryFile,
|
|
1342
1351
|
outputFolder,
|
|
1352
|
+
sourcemapOutputFolder,
|
|
1343
1353
|
platform,
|
|
1344
|
-
command.sourcemapOutput,
|
|
1345
1354
|
command.extraBundlerOptions
|
|
1346
|
-
)
|
|
1347
|
-
)
|
|
1355
|
+
);
|
|
1356
|
+
})
|
|
1348
1357
|
.then(async () => {
|
|
1349
|
-
const isHermesEnabled
|
|
1350
|
-
command.useHermes ||
|
|
1351
|
-
(platform === "android" && (await getAndroidHermesEnabled(command.gradleFile))) || // Check if we have to run hermes to compile JS to Byte Code if Hermes is enabled in build.gradle and we're releasing an Android build
|
|
1352
|
-
(platform === "ios" && (await getiOSHermesEnabled(command.podFile))); // Check if we have to run hermes to compile JS to Byte Code if Hermes is enabled in Podfile and we're releasing an iOS build
|
|
1358
|
+
const isHermes = await isHermesEnabled(command, platform);
|
|
1353
1359
|
|
|
1354
|
-
if (
|
|
1360
|
+
if (isHermes) {
|
|
1355
1361
|
log(chalk.cyan("\nRunning hermes compiler...\n"));
|
|
1356
1362
|
await runHermesEmitBinaryCommand(
|
|
1363
|
+
command,
|
|
1357
1364
|
bundleName,
|
|
1358
1365
|
outputFolder,
|
|
1359
|
-
|
|
1366
|
+
sourcemapOutputFolder,
|
|
1360
1367
|
command.extraHermesFlags,
|
|
1361
1368
|
command.gradleFile
|
|
1362
1369
|
);
|
|
@@ -1374,13 +1381,16 @@ export const releaseReact = (command: cli.IReleaseReactCommand): Promise<void> =
|
|
|
1374
1381
|
log(chalk.cyan("\nReleasing update contents to CodePush:\n"));
|
|
1375
1382
|
return release(releaseCommand);
|
|
1376
1383
|
})
|
|
1377
|
-
.then(() => {
|
|
1384
|
+
.then(async () => {
|
|
1378
1385
|
if (!command.outputDir) {
|
|
1379
|
-
deleteFolder(outputFolder);
|
|
1386
|
+
await deleteFolder(outputFolder);
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
if (!command.sourcemapOutput) {
|
|
1390
|
+
await deleteFolder(sourcemapOutputFolder);
|
|
1380
1391
|
}
|
|
1381
1392
|
})
|
|
1382
|
-
.catch((err: Error) => {
|
|
1383
|
-
deleteFolder(outputFolder);
|
|
1393
|
+
.catch(async (err: Error) => {
|
|
1384
1394
|
throw err;
|
|
1385
1395
|
})
|
|
1386
1396
|
);
|
|
@@ -1427,15 +1437,16 @@ function requestAccessKey(): Promise<string> {
|
|
|
1427
1437
|
});
|
|
1428
1438
|
}
|
|
1429
1439
|
|
|
1430
|
-
export const runReactNativeBundleCommand = (
|
|
1440
|
+
export const runReactNativeBundleCommand = async (
|
|
1441
|
+
command: cli.IReleaseReactCommand,
|
|
1431
1442
|
bundleName: string,
|
|
1432
1443
|
development: boolean,
|
|
1433
1444
|
entryFile: string,
|
|
1434
1445
|
outputFolder: string,
|
|
1446
|
+
sourcemapOutputFolder: string,
|
|
1435
1447
|
platform: string,
|
|
1436
|
-
sourcemapOutput: string,
|
|
1437
1448
|
extraBundlerOptions: string[]
|
|
1438
|
-
)
|
|
1449
|
+
) => {
|
|
1439
1450
|
const reactNativeBundleArgs: string[] = [];
|
|
1440
1451
|
const envNodeArgs: string = process.env.CODE_PUSH_NODE_ARGS;
|
|
1441
1452
|
|
|
@@ -1443,10 +1454,12 @@ export const runReactNativeBundleCommand = (
|
|
|
1443
1454
|
Array.prototype.push.apply(reactNativeBundleArgs, envNodeArgs.trim().split(/\s+/));
|
|
1444
1455
|
}
|
|
1445
1456
|
|
|
1446
|
-
const
|
|
1457
|
+
const reactNativePackagePath = getReactNativePackagePath();
|
|
1458
|
+
const oldCliPath = path.join(reactNativePackagePath, "local-cli", "cli.js");
|
|
1459
|
+
const cliPath = fs.existsSync(oldCliPath) ? oldCliPath : path.join(reactNativePackagePath, "cli.js");
|
|
1447
1460
|
|
|
1448
1461
|
Array.prototype.push.apply(reactNativeBundleArgs, [
|
|
1449
|
-
|
|
1462
|
+
cliPath,
|
|
1450
1463
|
"bundle",
|
|
1451
1464
|
"--assets-dest",
|
|
1452
1465
|
outputFolder,
|
|
@@ -1458,12 +1471,22 @@ export const runReactNativeBundleCommand = (
|
|
|
1458
1471
|
entryFile,
|
|
1459
1472
|
"--platform",
|
|
1460
1473
|
platform,
|
|
1474
|
+
"--reset-cache",
|
|
1461
1475
|
]);
|
|
1462
1476
|
|
|
1463
|
-
if (
|
|
1464
|
-
|
|
1477
|
+
if (sourcemapOutputFolder) {
|
|
1478
|
+
let bundleSourceMapOutput = sourcemapOutputFolder;
|
|
1479
|
+
if (!sourcemapOutputFolder.endsWith(".map")) {
|
|
1480
|
+
// user defined full path to source map. let's use that instead
|
|
1481
|
+
bundleSourceMapOutput = await getBundleSourceMapOutput(command, bundleName, sourcemapOutputFolder);
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
reactNativeBundleArgs.push("--sourcemap-output", bundleSourceMapOutput);
|
|
1465
1485
|
}
|
|
1466
1486
|
|
|
1487
|
+
const minifyValue = await getMinifyParams(command);
|
|
1488
|
+
Array.prototype.push.apply(reactNativeBundleArgs, minifyValue);
|
|
1489
|
+
|
|
1467
1490
|
if (extraBundlerOptions.length > 0) {
|
|
1468
1491
|
reactNativeBundleArgs.push(...extraBundlerOptions);
|
|
1469
1492
|
}
|
|
@@ -4,6 +4,9 @@ import * as path from "path";
|
|
|
4
4
|
import * as childProcess from "child_process";
|
|
5
5
|
import { coerce, compare, valid } from "semver";
|
|
6
6
|
import { fileDoesNotExistOrIsDirectory } from "./utils/file-utils";
|
|
7
|
+
import * as dotenv from "dotenv";
|
|
8
|
+
import { DotenvParseOutput } from "dotenv";
|
|
9
|
+
import * as cli from "../script/types/cli";
|
|
7
10
|
|
|
8
11
|
const g2js = require("gradle-to-js/lib/parser");
|
|
9
12
|
|
|
@@ -11,12 +14,46 @@ export function isValidVersion(version: string): boolean {
|
|
|
11
14
|
return !!valid(version) || /^\d+\.\d+$/.test(version);
|
|
12
15
|
}
|
|
13
16
|
|
|
17
|
+
export async function getBundleSourceMapOutput(command: cli.IReleaseReactCommand, bundleName: string, sourcemapOutputFolder: string) {
|
|
18
|
+
let bundleSourceMapOutput: string | undefined;
|
|
19
|
+
switch (command.platform) {
|
|
20
|
+
case "android": {
|
|
21
|
+
// see BundleHermesCTask -> resolvePackagerSourceMapFile
|
|
22
|
+
// for Hermes targeted bundles there are 2 source maps: "packager" (metro) and "compiler" (Hermes)
|
|
23
|
+
// Metro bundles use <bundleAssetName>.packager.map notation
|
|
24
|
+
const isHermes = await isHermesEnabled(command, command.platform);
|
|
25
|
+
if (isHermes) {
|
|
26
|
+
bundleSourceMapOutput = path.join(sourcemapOutputFolder, bundleName + ".packager.map");
|
|
27
|
+
} else {
|
|
28
|
+
bundleSourceMapOutput = path.join(sourcemapOutputFolder, bundleName + ".map");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
case "ios": {
|
|
34
|
+
// see react-native-xcode.sh
|
|
35
|
+
// to match js bundle generated by Xcode and by Revopush cli we must respect SOURCEMAP_FILE value
|
|
36
|
+
// because it appears as //# sourceMappingURL value in a js bundle
|
|
37
|
+
const xcodeDotEnvValue = getXcodeDotEnvValue("SOURCEMAP_FILE");
|
|
38
|
+
const sourceMapFilename = xcodeDotEnvValue ? path.basename(xcodeDotEnvValue) : bundleName + ".map";
|
|
39
|
+
|
|
40
|
+
bundleSourceMapOutput = path.join(sourcemapOutputFolder, sourceMapFilename);
|
|
41
|
+
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
default:
|
|
45
|
+
throw new Error('Platform must be either "android", "ios" or "windows".');
|
|
46
|
+
}
|
|
47
|
+
return bundleSourceMapOutput;
|
|
48
|
+
}
|
|
49
|
+
|
|
14
50
|
export async function runHermesEmitBinaryCommand(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
51
|
+
command: cli.IReleaseReactCommand,
|
|
52
|
+
bundleName: string,
|
|
53
|
+
outputFolder: string,
|
|
54
|
+
sourcemapOutputFolder: string,
|
|
55
|
+
extraHermesFlags: string[],
|
|
56
|
+
gradleFile: string
|
|
20
57
|
): Promise<void> {
|
|
21
58
|
const hermesArgs: string[] = [];
|
|
22
59
|
const envNodeArgs: string = process.env.CODE_PUSH_NODE_ARGS;
|
|
@@ -27,13 +64,14 @@ export async function runHermesEmitBinaryCommand(
|
|
|
27
64
|
|
|
28
65
|
Array.prototype.push.apply(hermesArgs, [
|
|
29
66
|
"-emit-binary",
|
|
67
|
+
"-O",
|
|
30
68
|
"-out",
|
|
31
69
|
path.join(outputFolder, bundleName + ".hbc"),
|
|
32
70
|
path.join(outputFolder, bundleName),
|
|
33
71
|
...extraHermesFlags,
|
|
34
72
|
]);
|
|
35
73
|
|
|
36
|
-
if (
|
|
74
|
+
if (sourcemapOutputFolder) {
|
|
37
75
|
hermesArgs.push("-output-source-map");
|
|
38
76
|
}
|
|
39
77
|
|
|
@@ -72,8 +110,8 @@ export async function runHermesEmitBinaryCommand(
|
|
|
72
110
|
});
|
|
73
111
|
});
|
|
74
112
|
});
|
|
75
|
-
}).then(() => {
|
|
76
|
-
if (!
|
|
113
|
+
}).then(async () => {
|
|
114
|
+
if (!sourcemapOutputFolder) {
|
|
77
115
|
// skip source map compose if source map is not enabled
|
|
78
116
|
return;
|
|
79
117
|
}
|
|
@@ -88,8 +126,33 @@ export async function runHermesEmitBinaryCommand(
|
|
|
88
126
|
throw new Error(`sourcemap file ${jsCompilerSourceMapFile} is not found`);
|
|
89
127
|
}
|
|
90
128
|
|
|
129
|
+
const platformSourceMapOutput = await getBundleSourceMapOutput(command, bundleName, sourcemapOutputFolder);
|
|
91
130
|
return new Promise((resolve, reject) => {
|
|
92
|
-
|
|
131
|
+
let bundleSourceMapOutput = sourcemapOutputFolder;
|
|
132
|
+
let combinedSourceMapOutput = sourcemapOutputFolder;
|
|
133
|
+
|
|
134
|
+
if (!sourcemapOutputFolder.endsWith(".map")) {
|
|
135
|
+
bundleSourceMapOutput = platformSourceMapOutput;
|
|
136
|
+
switch (command.platform) {
|
|
137
|
+
case "android": {
|
|
138
|
+
combinedSourceMapOutput = path.join(sourcemapOutputFolder, bundleName + ".map");
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
case "ios": {
|
|
142
|
+
combinedSourceMapOutput = bundleSourceMapOutput;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
default:
|
|
146
|
+
throw new Error('Platform must be either "android", "ios" or "windows".');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const composeSourceMapsArgs = [
|
|
150
|
+
composeSourceMapsPath,
|
|
151
|
+
bundleSourceMapOutput,
|
|
152
|
+
jsCompilerSourceMapFile,
|
|
153
|
+
"-o",
|
|
154
|
+
combinedSourceMapOutput,
|
|
155
|
+
];
|
|
93
156
|
|
|
94
157
|
// https://github.com/facebook/react-native/blob/master/react.gradle#L211
|
|
95
158
|
// https://github.com/facebook/react-native/blob/master/scripts/react-native-xcode.sh#L178
|
|
@@ -124,6 +187,40 @@ export async function runHermesEmitBinaryCommand(
|
|
|
124
187
|
});
|
|
125
188
|
}
|
|
126
189
|
|
|
190
|
+
export function getXcodeDotEnvValue(key: string): string | undefined {
|
|
191
|
+
const xcodeEnvs = loadEnvAsMap([path.join("ios", ".xcode.env.local"), path.join("ios", ".xcode.env.local")]);
|
|
192
|
+
return xcodeEnvs.get(key);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export async function getMinifyParams(command: cli.IReleaseReactCommand) {
|
|
196
|
+
const isHermes = await isHermesEnabled(command);
|
|
197
|
+
|
|
198
|
+
switch (command.platform) {
|
|
199
|
+
case "android": {
|
|
200
|
+
// android always explicitly pass --minify true/false
|
|
201
|
+
// TaskConfiguration it.minifyEnabled.set(!isHermesEnabledInThisVariant)
|
|
202
|
+
return ["--minify", !isHermes];
|
|
203
|
+
}
|
|
204
|
+
case "ios": {
|
|
205
|
+
//if [[ $USE_HERMES != false && $DEV == false ]]; then
|
|
206
|
+
// EXTRA_ARGS+=("--minify" "false")
|
|
207
|
+
// fi
|
|
208
|
+
// ios does pass --minify false only if Hermes enables and does pass anything otherwise
|
|
209
|
+
return isHermes ? ["--minify", false] : [];
|
|
210
|
+
}
|
|
211
|
+
default:
|
|
212
|
+
throw new Error('Platform must be either "android", "ios" or "windows".');
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export async function isHermesEnabled(command: cli.IReleaseReactCommand, platform: string = command.platform.toLowerCase()) {
|
|
217
|
+
// Check if we have to run hermes to compile JS to Byte Code if Hermes is enabled in Podfile and we're releasing an iOS build
|
|
218
|
+
const isAndroidHermesEnabled = await getAndroidHermesEnabled(command.gradleFile);
|
|
219
|
+
const isIOSHermesEnabled = getiOSHermesEnabled(command.podFile);
|
|
220
|
+
|
|
221
|
+
return command.useHermes || (platform === "android" && isAndroidHermesEnabled) || (platform === "ios" && isIOSHermesEnabled);
|
|
222
|
+
}
|
|
223
|
+
|
|
127
224
|
function parseBuildGradleFile(gradleFile: string) {
|
|
128
225
|
let buildGradlePath: string = path.join("android", "app");
|
|
129
226
|
if (gradleFile) {
|
|
@@ -142,6 +239,47 @@ function parseBuildGradleFile(gradleFile: string) {
|
|
|
142
239
|
});
|
|
143
240
|
}
|
|
144
241
|
|
|
242
|
+
function parseGradlePropertiesFile(gradleFile: string): Record<string, string> {
|
|
243
|
+
let gradlePropsPath: string = path.join("android", "gradle.properties");
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
if (gradleFile) {
|
|
247
|
+
const base = gradleFile;
|
|
248
|
+
const stat = fs.lstatSync(base);
|
|
249
|
+
|
|
250
|
+
if (stat.isDirectory()) {
|
|
251
|
+
if (path.basename(base) === "app") {
|
|
252
|
+
gradlePropsPath = path.join(base, "..", "gradle.properties");
|
|
253
|
+
} else {
|
|
254
|
+
gradlePropsPath = path.join(base, "gradle.properties");
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
257
|
+
gradlePropsPath = path.join(path.dirname(base), "..", "gradle.properties");
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
} catch {}
|
|
261
|
+
|
|
262
|
+
gradlePropsPath = path.normalize(gradlePropsPath);
|
|
263
|
+
|
|
264
|
+
if (fileDoesNotExistOrIsDirectory(gradlePropsPath)) {
|
|
265
|
+
throw new Error(`Unable to find gradle.properties file "${gradlePropsPath}".`);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const text = fs.readFileSync(gradlePropsPath, "utf8");
|
|
269
|
+
const props: Record<string, string> = {};
|
|
270
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
271
|
+
const line = rawLine.trim();
|
|
272
|
+
if (!line || line.startsWith("#")) continue;
|
|
273
|
+
const m = line.match(/^([^=\s]+)\s*=\s*(.*)$/);
|
|
274
|
+
if (m) {
|
|
275
|
+
const key = m[1].trim();
|
|
276
|
+
const val = m[2].trim();
|
|
277
|
+
props[key] = val;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return props;
|
|
281
|
+
}
|
|
282
|
+
|
|
145
283
|
async function getHermesCommandFromGradle(gradleFile: string): Promise<string> {
|
|
146
284
|
const buildGradle: any = await parseBuildGradleFile(gradleFile);
|
|
147
285
|
const hermesCommandProperty: any = Array.from(buildGradle["project.ext.react"] || []).find((prop: string) =>
|
|
@@ -154,13 +292,28 @@ async function getHermesCommandFromGradle(gradleFile: string): Promise<string> {
|
|
|
154
292
|
}
|
|
155
293
|
}
|
|
156
294
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
295
|
+
async function getAndroidHermesEnabled(gradleFile: string): Promise<boolean> {
|
|
296
|
+
try {
|
|
297
|
+
const props = parseGradlePropertiesFile(gradleFile);
|
|
298
|
+
if (typeof props.hermesEnabled !== "undefined") {
|
|
299
|
+
const v = String(props.hermesEnabled).trim().toLowerCase();
|
|
300
|
+
if (v === "true") return true;
|
|
301
|
+
if (v === "false") return false;
|
|
302
|
+
}
|
|
303
|
+
} catch {}
|
|
304
|
+
|
|
305
|
+
try {
|
|
306
|
+
const buildGradle: any = await parseBuildGradleFile(gradleFile);
|
|
307
|
+
const lines: string[] = Array.from(buildGradle["project.ext.react"] || []);
|
|
308
|
+
if (lines.some((l) => /\benableHermes\s*:\s*true\b/.test(l))) return true;
|
|
309
|
+
if (lines.some((l) => /\benableHermes\s*:\s*false\b/.test(l))) return false;
|
|
310
|
+
} catch {}
|
|
311
|
+
|
|
312
|
+
const rnVersion = coerce(getReactNativeVersion())?.version;
|
|
313
|
+
return rnVersion && compare(rnVersion, "0.70.0") >= 0;
|
|
161
314
|
}
|
|
162
315
|
|
|
163
|
-
|
|
316
|
+
function getiOSHermesEnabled(podFile: string): boolean {
|
|
164
317
|
let podPath = path.join("ios", "Podfile");
|
|
165
318
|
if (podFile) {
|
|
166
319
|
podPath = podFile;
|
|
@@ -171,12 +324,33 @@ export function getiOSHermesEnabled(podFile: string): boolean {
|
|
|
171
324
|
|
|
172
325
|
try {
|
|
173
326
|
const podFileContents = fs.readFileSync(podPath).toString();
|
|
174
|
-
|
|
327
|
+
|
|
328
|
+
const hasTrue = /([^#\n]*:?hermes_enabled(\s+|\n+)?(=>|:)(\s+|\n+)?true)/.test(podFileContents);
|
|
329
|
+
if (hasTrue) return true;
|
|
330
|
+
|
|
331
|
+
const hasFalse = /([^#\n]*:?hermes_enabled(\s+|\n+)?(=>|:)(\s+|\n+)?false)/.test(podFileContents);
|
|
332
|
+
if (hasFalse) return false;
|
|
333
|
+
|
|
334
|
+
const rnVersion = coerce(getReactNativeVersion())?.version;
|
|
335
|
+
return rnVersion && compare(rnVersion, "0.70.0") >= 0;
|
|
175
336
|
} catch (error) {
|
|
176
337
|
throw error;
|
|
177
338
|
}
|
|
178
339
|
}
|
|
179
340
|
|
|
341
|
+
function loadEnvAsMap(envPaths = []): Map<string, string | undefined> {
|
|
342
|
+
const merged: DotenvParseOutput = {};
|
|
343
|
+
|
|
344
|
+
for (const envPath of envPaths) {
|
|
345
|
+
if (fs.existsSync(envPath)) {
|
|
346
|
+
Object.assign(merged, dotenv.parse(fs.readFileSync(envPath))); // later files override earlier ones
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// fallback to process.env for anything missing
|
|
351
|
+
return new Map([...Object.entries(process.env), ...Object.entries(merged)]);
|
|
352
|
+
}
|
|
353
|
+
|
|
180
354
|
function getHermesOSBin(): string {
|
|
181
355
|
switch (process.platform) {
|
|
182
356
|
case "win32":
|
|
@@ -211,7 +385,8 @@ async function getHermesCommand(gradleFile: string): Promise<string> {
|
|
|
211
385
|
}
|
|
212
386
|
};
|
|
213
387
|
// Hermes is bundled with react-native since 0.69
|
|
214
|
-
const
|
|
388
|
+
const reactNativePath = getReactNativePackagePath();
|
|
389
|
+
const bundledHermesEngine = path.join(reactNativePath, "sdks", "hermesc", getHermesOSBin(), getHermesOSExe());
|
|
215
390
|
if (fileExists(bundledHermesEngine)) {
|
|
216
391
|
return bundledHermesEngine;
|
|
217
392
|
}
|
|
@@ -220,12 +395,14 @@ async function getHermesCommand(gradleFile: string): Promise<string> {
|
|
|
220
395
|
if (gradleHermesCommand) {
|
|
221
396
|
return path.join("android", "app", gradleHermesCommand.replace("%OS-BIN%", getHermesOSBin()));
|
|
222
397
|
} else {
|
|
398
|
+
const nodeModulesPath = getNodeModulesPath(reactNativePath);
|
|
399
|
+
|
|
223
400
|
// assume if hermes-engine exists it should be used instead of hermesvm
|
|
224
|
-
const hermesEngine = path.join(
|
|
401
|
+
const hermesEngine = path.join(nodeModulesPath, "hermes-engine", getHermesOSBin(), getHermesOSExe());
|
|
225
402
|
if (fileExists(hermesEngine)) {
|
|
226
403
|
return hermesEngine;
|
|
227
404
|
}
|
|
228
|
-
return path.join(
|
|
405
|
+
return path.join(nodeModulesPath, "hermesvm", getHermesOSBin(), "hermes");
|
|
229
406
|
}
|
|
230
407
|
}
|
|
231
408
|
|
|
@@ -238,7 +415,16 @@ function getComposeSourceMapsPath(): string {
|
|
|
238
415
|
return null;
|
|
239
416
|
}
|
|
240
417
|
|
|
241
|
-
function
|
|
418
|
+
function getNodeModulesPath(reactNativePath: string): string {
|
|
419
|
+
const nodeModulesPath = path.dirname(reactNativePath)
|
|
420
|
+
if (directoryExistsSync(nodeModulesPath)) {
|
|
421
|
+
return nodeModulesPath;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return path.join("node_modules");
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export function getReactNativePackagePath(): string {
|
|
242
428
|
const result = childProcess.spawnSync("node", ["--print", "require.resolve('react-native/package.json')"]);
|
|
243
429
|
const packagePath = path.dirname(result.stdout.toString());
|
|
244
430
|
if (result.status === 0 && directoryExistsSync(packagePath)) {
|
|
@@ -280,4 +466,4 @@ export function getReactNativeVersion(): string {
|
|
|
280
466
|
(projectPackageJson.dependencies && projectPackageJson.dependencies["react-native"]) ||
|
|
281
467
|
(projectPackageJson.devDependencies && projectPackageJson.devDependencies["react-native"])
|
|
282
468
|
);
|
|
283
|
-
}
|
|
469
|
+
}
|