@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.
@@ -3,35 +3,34 @@
3
3
  // Licensed under the MIT License.
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.runReactNativeBundleCommand = exports.releaseReact = exports.release = exports.execute = exports.deploymentList = exports.createEmptyTempReleaseFolder = exports.confirm = exports.execSync = exports.spawn = exports.sdk = exports.log = void 0;
6
- const AccountManager = require("./management-sdk");
7
6
  const childProcess = require("child_process");
8
7
  const debug_1 = require("./commands/debug");
9
8
  const fs = require("fs");
10
9
  const chalk = require("chalk");
11
- const g2js = require("gradle-to-js/lib/parser");
12
10
  const moment = require("moment");
13
- const opener = require("opener");
14
11
  const os = require("os");
15
12
  const path = require("path");
16
- const plist = require("plist");
17
- const progress = require("progress");
18
- const prompt = require("prompt");
19
13
  const Q = require("q");
20
- const rimraf = require("rimraf");
21
14
  const semver = require("semver");
22
- const Table = require("cli-table");
23
- const which = require("which");
24
- const wordwrap = require("wordwrap");
25
15
  const cli = require("../script/types/cli");
26
16
  const sign_1 = require("./sign");
27
- const xcode = require("xcode");
28
17
  const react_native_utils_1 = require("./react-native-utils");
29
18
  const file_utils_1 = require("./utils/file-utils");
19
+ const AccountManager = require("./management-sdk");
20
+ const wordwrap = require("wordwrap");
21
+ var Promise = Q.Promise;
22
+ const g2js = require("gradle-to-js/lib/parser");
23
+ const opener = require("opener");
24
+ const plist = require("plist");
25
+ const progress = require("progress");
26
+ const prompt = require("prompt");
27
+ const rimraf = require("rimraf");
28
+ const Table = require("cli-table");
29
+ const xcode = require("xcode");
30
30
  const configFilePath = path.join(process.env.LOCALAPPDATA || process.env.HOME, ".revopush.config");
31
31
  const emailValidator = require("email-validator");
32
32
  const packageJson = require("../../package.json");
33
33
  const parseXml = Q.denodeify(require("xml2js").parseString);
34
- var Promise = Q.Promise;
35
34
  const properties = require("properties");
36
35
  const CLI_HEADERS = {
37
36
  "X-CodePush-CLI-Version": packageJson.version,
@@ -237,12 +236,11 @@ function deploymentHistoryClear(command) {
237
236
  const deploymentList = (command, showPackage = true) => {
238
237
  throwForInvalidOutputFormat(command.format);
239
238
  let deployments;
240
- const DEPLOYMENTS_MAX_LENGTH = 10; // do not take metrics if number of deployment higher than this
241
239
  return exports.sdk
242
240
  .getDeployments(command.appName)
243
241
  .then((retrievedDeployments) => {
244
242
  deployments = retrievedDeployments;
245
- if (showPackage && deployments.length < DEPLOYMENTS_MAX_LENGTH) {
243
+ if (showPackage) {
246
244
  const metricsPromises = deployments.map((deployment) => {
247
245
  if (deployment.package) {
248
246
  return exports.sdk.getDeploymentMetrics(command.appName, deployment.name).then((metrics) => {
@@ -1020,13 +1018,14 @@ const releaseReact = (command) => {
1020
1018
  let bundleName = command.bundleName;
1021
1019
  let entryFile = command.entryFile;
1022
1020
  const outputFolder = command.outputDir || path.join(os.tmpdir(), "CodePush");
1021
+ const sourcemapOutputFolder = command.sourcemapOutput || path.join(os.tmpdir(), "CodePushSourceMap");
1023
1022
  const platform = (command.platform = command.platform.toLowerCase());
1024
1023
  const releaseCommand = command;
1025
1024
  // Check for app and deployment exist before releasing an update.
1026
1025
  // This validation helps to save about 1 minute or more in case user has typed wrong app or deployment name.
1027
1026
  return (exports.sdk
1028
1027
  .getDeployment(command.appName, command.deploymentName)
1029
- .then(() => {
1028
+ .then(async () => {
1030
1029
  releaseCommand.package = outputFolder;
1031
1030
  switch (platform) {
1032
1031
  case "android":
@@ -1070,8 +1069,9 @@ const releaseReact = (command) => {
1070
1069
  const appVersionPromise = command.appStoreVersion
1071
1070
  ? Q(command.appStoreVersion)
1072
1071
  : getReactNativeProjectAppVersion(command, projectName);
1073
- if (command.sourcemapOutput && !command.sourcemapOutput.endsWith(".map")) {
1074
- command.sourcemapOutput = path.join(command.sourcemapOutput, bundleName + ".map");
1072
+ if (!sourcemapOutputFolder.endsWith(".map") && !command.sourcemapOutput) {
1073
+ // create tmp dir only if no dir was given by user. User must crete a directory if --sourcemapOutput is passes
1074
+ await (0, exports.createEmptyTempReleaseFolder)(sourcemapOutputFolder);
1075
1075
  }
1076
1076
  return appVersionPromise;
1077
1077
  })
@@ -1083,14 +1083,14 @@ const releaseReact = (command) => {
1083
1083
  // This is needed to clear the react native bundler cache:
1084
1084
  // https://github.com/facebook/react-native/issues/4289
1085
1085
  .then(() => deleteFolder(`${os.tmpdir()}/react-*`))
1086
- .then(() => (0, exports.runReactNativeBundleCommand)(bundleName, command.development || false, entryFile, outputFolder, platform, command.sourcemapOutput, command.extraBundlerOptions))
1087
1086
  .then(async () => {
1088
- const isHermesEnabled = command.useHermes ||
1089
- (platform === "android" && (await (0, react_native_utils_1.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
1090
- (platform === "ios" && (await (0, react_native_utils_1.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
1091
- if (isHermesEnabled) {
1087
+ await (0, exports.runReactNativeBundleCommand)(command, bundleName, command.development || false, entryFile, outputFolder, sourcemapOutputFolder, platform, command.extraBundlerOptions);
1088
+ })
1089
+ .then(async () => {
1090
+ const isHermes = await (0, react_native_utils_1.isHermesEnabled)(command, platform);
1091
+ if (isHermes) {
1092
1092
  (0, exports.log)(chalk.cyan("\nRunning hermes compiler...\n"));
1093
- await (0, react_native_utils_1.runHermesEmitBinaryCommand)(bundleName, outputFolder, command.sourcemapOutput, command.extraHermesFlags, command.gradleFile);
1093
+ await (0, react_native_utils_1.runHermesEmitBinaryCommand)(command, bundleName, outputFolder, sourcemapOutputFolder, command.extraHermesFlags, command.gradleFile);
1094
1094
  }
1095
1095
  })
1096
1096
  .then(async () => {
@@ -1106,13 +1106,15 @@ const releaseReact = (command) => {
1106
1106
  (0, exports.log)(chalk.cyan("\nReleasing update contents to CodePush:\n"));
1107
1107
  return (0, exports.release)(releaseCommand);
1108
1108
  })
1109
- .then(() => {
1109
+ .then(async () => {
1110
1110
  if (!command.outputDir) {
1111
- deleteFolder(outputFolder);
1111
+ await deleteFolder(outputFolder);
1112
+ }
1113
+ if (!command.sourcemapOutput) {
1114
+ await deleteFolder(sourcemapOutputFolder);
1112
1115
  }
1113
1116
  })
1114
- .catch((err) => {
1115
- deleteFolder(outputFolder);
1117
+ .catch(async (err) => {
1116
1118
  throw err;
1117
1119
  }));
1118
1120
  };
@@ -1149,15 +1151,17 @@ function requestAccessKey() {
1149
1151
  });
1150
1152
  });
1151
1153
  }
1152
- const runReactNativeBundleCommand = (bundleName, development, entryFile, outputFolder, platform, sourcemapOutput, extraBundlerOptions) => {
1154
+ const runReactNativeBundleCommand = async (command, bundleName, development, entryFile, outputFolder, sourcemapOutputFolder, platform, extraBundlerOptions) => {
1153
1155
  const reactNativeBundleArgs = [];
1154
1156
  const envNodeArgs = process.env.CODE_PUSH_NODE_ARGS;
1155
1157
  if (typeof envNodeArgs !== "undefined") {
1156
1158
  Array.prototype.push.apply(reactNativeBundleArgs, envNodeArgs.trim().split(/\s+/));
1157
1159
  }
1158
- const isOldCLI = fs.existsSync(path.join("node_modules", "react-native", "local-cli", "cli.js"));
1160
+ const reactNativePackagePath = (0, react_native_utils_1.getReactNativePackagePath)();
1161
+ const oldCliPath = path.join(reactNativePackagePath, "local-cli", "cli.js");
1162
+ const cliPath = fs.existsSync(oldCliPath) ? oldCliPath : path.join(reactNativePackagePath, "cli.js");
1159
1163
  Array.prototype.push.apply(reactNativeBundleArgs, [
1160
- isOldCLI ? path.join("node_modules", "react-native", "local-cli", "cli.js") : path.join("node_modules", "react-native", "cli.js"),
1164
+ cliPath,
1161
1165
  "bundle",
1162
1166
  "--assets-dest",
1163
1167
  outputFolder,
@@ -1169,10 +1173,18 @@ const runReactNativeBundleCommand = (bundleName, development, entryFile, outputF
1169
1173
  entryFile,
1170
1174
  "--platform",
1171
1175
  platform,
1176
+ "--reset-cache",
1172
1177
  ]);
1173
- if (sourcemapOutput) {
1174
- reactNativeBundleArgs.push("--sourcemap-output", sourcemapOutput);
1178
+ if (sourcemapOutputFolder) {
1179
+ let bundleSourceMapOutput = sourcemapOutputFolder;
1180
+ if (!sourcemapOutputFolder.endsWith(".map")) {
1181
+ // user defined full path to source map. let's use that instead
1182
+ bundleSourceMapOutput = await (0, react_native_utils_1.getBundleSourceMapOutput)(command, bundleName, sourcemapOutputFolder);
1183
+ }
1184
+ reactNativeBundleArgs.push("--sourcemap-output", bundleSourceMapOutput);
1175
1185
  }
1186
+ const minifyValue = await (0, react_native_utils_1.getMinifyParams)(command);
1187
+ Array.prototype.push.apply(reactNativeBundleArgs, minifyValue);
1176
1188
  if (extraBundlerOptions.length > 0) {
1177
1189
  reactNativeBundleArgs.push(...extraBundlerOptions);
1178
1190
  }
@@ -1,18 +1,50 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getReactNativeVersion = exports.directoryExistsSync = exports.getiOSHermesEnabled = exports.getAndroidHermesEnabled = exports.runHermesEmitBinaryCommand = exports.isValidVersion = void 0;
3
+ exports.getReactNativeVersion = exports.directoryExistsSync = exports.getReactNativePackagePath = exports.isHermesEnabled = exports.getMinifyParams = exports.getXcodeDotEnvValue = exports.runHermesEmitBinaryCommand = exports.getBundleSourceMapOutput = exports.isValidVersion = void 0;
4
4
  const fs = require("fs");
5
5
  const chalk = require("chalk");
6
6
  const path = require("path");
7
7
  const childProcess = require("child_process");
8
8
  const semver_1 = require("semver");
9
9
  const file_utils_1 = require("./utils/file-utils");
10
+ const dotenv = require("dotenv");
10
11
  const g2js = require("gradle-to-js/lib/parser");
11
12
  function isValidVersion(version) {
12
13
  return !!(0, semver_1.valid)(version) || /^\d+\.\d+$/.test(version);
13
14
  }
14
15
  exports.isValidVersion = isValidVersion;
15
- async function runHermesEmitBinaryCommand(bundleName, outputFolder, sourcemapOutput, extraHermesFlags, gradleFile) {
16
+ async function getBundleSourceMapOutput(command, bundleName, sourcemapOutputFolder) {
17
+ let bundleSourceMapOutput;
18
+ switch (command.platform) {
19
+ case "android": {
20
+ // see BundleHermesCTask -> resolvePackagerSourceMapFile
21
+ // for Hermes targeted bundles there are 2 source maps: "packager" (metro) and "compiler" (Hermes)
22
+ // Metro bundles use <bundleAssetName>.packager.map notation
23
+ const isHermes = await isHermesEnabled(command, command.platform);
24
+ if (isHermes) {
25
+ bundleSourceMapOutput = path.join(sourcemapOutputFolder, bundleName + ".packager.map");
26
+ }
27
+ else {
28
+ bundleSourceMapOutput = path.join(sourcemapOutputFolder, bundleName + ".map");
29
+ }
30
+ break;
31
+ }
32
+ case "ios": {
33
+ // see react-native-xcode.sh
34
+ // to match js bundle generated by Xcode and by Revopush cli we must respect SOURCEMAP_FILE value
35
+ // because it appears as //# sourceMappingURL value in a js bundle
36
+ const xcodeDotEnvValue = getXcodeDotEnvValue("SOURCEMAP_FILE");
37
+ const sourceMapFilename = xcodeDotEnvValue ? path.basename(xcodeDotEnvValue) : bundleName + ".map";
38
+ bundleSourceMapOutput = path.join(sourcemapOutputFolder, sourceMapFilename);
39
+ break;
40
+ }
41
+ default:
42
+ throw new Error('Platform must be either "android", "ios" or "windows".');
43
+ }
44
+ return bundleSourceMapOutput;
45
+ }
46
+ exports.getBundleSourceMapOutput = getBundleSourceMapOutput;
47
+ async function runHermesEmitBinaryCommand(command, bundleName, outputFolder, sourcemapOutputFolder, extraHermesFlags, gradleFile) {
16
48
  const hermesArgs = [];
17
49
  const envNodeArgs = process.env.CODE_PUSH_NODE_ARGS;
18
50
  if (typeof envNodeArgs !== "undefined") {
@@ -20,12 +52,13 @@ async function runHermesEmitBinaryCommand(bundleName, outputFolder, sourcemapOut
20
52
  }
21
53
  Array.prototype.push.apply(hermesArgs, [
22
54
  "-emit-binary",
55
+ "-O",
23
56
  "-out",
24
57
  path.join(outputFolder, bundleName + ".hbc"),
25
58
  path.join(outputFolder, bundleName),
26
59
  ...extraHermesFlags,
27
60
  ]);
28
- if (sourcemapOutput) {
61
+ if (sourcemapOutputFolder) {
29
62
  hermesArgs.push("-output-source-map");
30
63
  }
31
64
  console.log(chalk.cyan("Converting JS bundle to byte code via Hermes, running command:\n"));
@@ -60,8 +93,8 @@ async function runHermesEmitBinaryCommand(bundleName, outputFolder, sourcemapOut
60
93
  });
61
94
  });
62
95
  });
63
- }).then(() => {
64
- if (!sourcemapOutput) {
96
+ }).then(async () => {
97
+ if (!sourcemapOutputFolder) {
65
98
  // skip source map compose if source map is not enabled
66
99
  return;
67
100
  }
@@ -73,8 +106,32 @@ async function runHermesEmitBinaryCommand(bundleName, outputFolder, sourcemapOut
73
106
  if (!fs.existsSync(jsCompilerSourceMapFile)) {
74
107
  throw new Error(`sourcemap file ${jsCompilerSourceMapFile} is not found`);
75
108
  }
109
+ const platformSourceMapOutput = await getBundleSourceMapOutput(command, bundleName, sourcemapOutputFolder);
76
110
  return new Promise((resolve, reject) => {
77
- const composeSourceMapsArgs = [composeSourceMapsPath, sourcemapOutput, jsCompilerSourceMapFile, "-o", sourcemapOutput];
111
+ let bundleSourceMapOutput = sourcemapOutputFolder;
112
+ let combinedSourceMapOutput = sourcemapOutputFolder;
113
+ if (!sourcemapOutputFolder.endsWith(".map")) {
114
+ bundleSourceMapOutput = platformSourceMapOutput;
115
+ switch (command.platform) {
116
+ case "android": {
117
+ combinedSourceMapOutput = path.join(sourcemapOutputFolder, bundleName + ".map");
118
+ break;
119
+ }
120
+ case "ios": {
121
+ combinedSourceMapOutput = bundleSourceMapOutput;
122
+ break;
123
+ }
124
+ default:
125
+ throw new Error('Platform must be either "android", "ios" or "windows".');
126
+ }
127
+ }
128
+ const composeSourceMapsArgs = [
129
+ composeSourceMapsPath,
130
+ bundleSourceMapOutput,
131
+ jsCompilerSourceMapFile,
132
+ "-o",
133
+ combinedSourceMapOutput,
134
+ ];
78
135
  // https://github.com/facebook/react-native/blob/master/react.gradle#L211
79
136
  // https://github.com/facebook/react-native/blob/master/scripts/react-native-xcode.sh#L178
80
137
  // packager.sourcemap.map + hbc.sourcemap.map = sourcemap.map
@@ -103,6 +160,38 @@ async function runHermesEmitBinaryCommand(bundleName, outputFolder, sourcemapOut
103
160
  });
104
161
  }
105
162
  exports.runHermesEmitBinaryCommand = runHermesEmitBinaryCommand;
163
+ function getXcodeDotEnvValue(key) {
164
+ const xcodeEnvs = loadEnvAsMap([path.join("ios", ".xcode.env.local"), path.join("ios", ".xcode.env.local")]);
165
+ return xcodeEnvs.get(key);
166
+ }
167
+ exports.getXcodeDotEnvValue = getXcodeDotEnvValue;
168
+ async function getMinifyParams(command) {
169
+ const isHermes = await isHermesEnabled(command);
170
+ switch (command.platform) {
171
+ case "android": {
172
+ // android always explicitly pass --minify true/false
173
+ // TaskConfiguration it.minifyEnabled.set(!isHermesEnabledInThisVariant)
174
+ return ["--minify", !isHermes];
175
+ }
176
+ case "ios": {
177
+ //if [[ $USE_HERMES != false && $DEV == false ]]; then
178
+ // EXTRA_ARGS+=("--minify" "false")
179
+ // fi
180
+ // ios does pass --minify false only if Hermes enables and does pass anything otherwise
181
+ return isHermes ? ["--minify", false] : [];
182
+ }
183
+ default:
184
+ throw new Error('Platform must be either "android", "ios" or "windows".');
185
+ }
186
+ }
187
+ exports.getMinifyParams = getMinifyParams;
188
+ async function isHermesEnabled(command, platform = command.platform.toLowerCase()) {
189
+ // 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
190
+ const isAndroidHermesEnabled = await getAndroidHermesEnabled(command.gradleFile);
191
+ const isIOSHermesEnabled = getiOSHermesEnabled(command.podFile);
192
+ return command.useHermes || (platform === "android" && isAndroidHermesEnabled) || (platform === "ios" && isIOSHermesEnabled);
193
+ }
194
+ exports.isHermesEnabled = isHermesEnabled;
106
195
  function parseBuildGradleFile(gradleFile) {
107
196
  let buildGradlePath = path.join("android", "app");
108
197
  if (gradleFile) {
@@ -118,6 +207,45 @@ function parseBuildGradleFile(gradleFile) {
118
207
  throw new Error(`Unable to parse the "${buildGradlePath}" file. Please ensure it is a well-formed Gradle file.`);
119
208
  });
120
209
  }
210
+ function parseGradlePropertiesFile(gradleFile) {
211
+ let gradlePropsPath = path.join("android", "gradle.properties");
212
+ try {
213
+ if (gradleFile) {
214
+ const base = gradleFile;
215
+ const stat = fs.lstatSync(base);
216
+ if (stat.isDirectory()) {
217
+ if (path.basename(base) === "app") {
218
+ gradlePropsPath = path.join(base, "..", "gradle.properties");
219
+ }
220
+ else {
221
+ gradlePropsPath = path.join(base, "gradle.properties");
222
+ }
223
+ }
224
+ else {
225
+ gradlePropsPath = path.join(path.dirname(base), "..", "gradle.properties");
226
+ }
227
+ }
228
+ }
229
+ catch { }
230
+ gradlePropsPath = path.normalize(gradlePropsPath);
231
+ if ((0, file_utils_1.fileDoesNotExistOrIsDirectory)(gradlePropsPath)) {
232
+ throw new Error(`Unable to find gradle.properties file "${gradlePropsPath}".`);
233
+ }
234
+ const text = fs.readFileSync(gradlePropsPath, "utf8");
235
+ const props = {};
236
+ for (const rawLine of text.split(/\r?\n/)) {
237
+ const line = rawLine.trim();
238
+ if (!line || line.startsWith("#"))
239
+ continue;
240
+ const m = line.match(/^([^=\s]+)\s*=\s*(.*)$/);
241
+ if (m) {
242
+ const key = m[1].trim();
243
+ const val = m[2].trim();
244
+ props[key] = val;
245
+ }
246
+ }
247
+ return props;
248
+ }
121
249
  async function getHermesCommandFromGradle(gradleFile) {
122
250
  const buildGradle = await parseBuildGradleFile(gradleFile);
123
251
  const hermesCommandProperty = Array.from(buildGradle["project.ext.react"] || []).find((prop) => prop.trim().startsWith("hermesCommand:"));
@@ -128,12 +256,30 @@ async function getHermesCommandFromGradle(gradleFile) {
128
256
  return "";
129
257
  }
130
258
  }
131
- function getAndroidHermesEnabled(gradleFile) {
132
- return parseBuildGradleFile(gradleFile).then((buildGradle) => {
133
- return Array.from(buildGradle["project.ext.react"] || []).some((line) => /^enableHermes\s{0,}:\s{0,}true/.test(line));
134
- });
259
+ async function getAndroidHermesEnabled(gradleFile) {
260
+ try {
261
+ const props = parseGradlePropertiesFile(gradleFile);
262
+ if (typeof props.hermesEnabled !== "undefined") {
263
+ const v = String(props.hermesEnabled).trim().toLowerCase();
264
+ if (v === "true")
265
+ return true;
266
+ if (v === "false")
267
+ return false;
268
+ }
269
+ }
270
+ catch { }
271
+ try {
272
+ const buildGradle = await parseBuildGradleFile(gradleFile);
273
+ const lines = Array.from(buildGradle["project.ext.react"] || []);
274
+ if (lines.some((l) => /\benableHermes\s*:\s*true\b/.test(l)))
275
+ return true;
276
+ if (lines.some((l) => /\benableHermes\s*:\s*false\b/.test(l)))
277
+ return false;
278
+ }
279
+ catch { }
280
+ const rnVersion = (0, semver_1.coerce)(getReactNativeVersion())?.version;
281
+ return rnVersion && (0, semver_1.compare)(rnVersion, "0.70.0") >= 0;
135
282
  }
136
- exports.getAndroidHermesEnabled = getAndroidHermesEnabled;
137
283
  function getiOSHermesEnabled(podFile) {
138
284
  let podPath = path.join("ios", "Podfile");
139
285
  if (podFile) {
@@ -144,13 +290,29 @@ function getiOSHermesEnabled(podFile) {
144
290
  }
145
291
  try {
146
292
  const podFileContents = fs.readFileSync(podPath).toString();
147
- return /([^#\n]*:?hermes_enabled(\s+|\n+)?(=>|:)(\s+|\n+)?true)/.test(podFileContents);
293
+ const hasTrue = /([^#\n]*:?hermes_enabled(\s+|\n+)?(=>|:)(\s+|\n+)?true)/.test(podFileContents);
294
+ if (hasTrue)
295
+ return true;
296
+ const hasFalse = /([^#\n]*:?hermes_enabled(\s+|\n+)?(=>|:)(\s+|\n+)?false)/.test(podFileContents);
297
+ if (hasFalse)
298
+ return false;
299
+ const rnVersion = (0, semver_1.coerce)(getReactNativeVersion())?.version;
300
+ return rnVersion && (0, semver_1.compare)(rnVersion, "0.70.0") >= 0;
148
301
  }
149
302
  catch (error) {
150
303
  throw error;
151
304
  }
152
305
  }
153
- exports.getiOSHermesEnabled = getiOSHermesEnabled;
306
+ function loadEnvAsMap(envPaths = []) {
307
+ const merged = {};
308
+ for (const envPath of envPaths) {
309
+ if (fs.existsSync(envPath)) {
310
+ Object.assign(merged, dotenv.parse(fs.readFileSync(envPath))); // later files override earlier ones
311
+ }
312
+ }
313
+ // fallback to process.env for anything missing
314
+ return new Map([...Object.entries(process.env), ...Object.entries(merged)]);
315
+ }
154
316
  function getHermesOSBin() {
155
317
  switch (process.platform) {
156
318
  case "win32":
@@ -184,7 +346,8 @@ async function getHermesCommand(gradleFile) {
184
346
  }
185
347
  };
186
348
  // Hermes is bundled with react-native since 0.69
187
- const bundledHermesEngine = path.join(getReactNativePackagePath(), "sdks", "hermesc", getHermesOSBin(), getHermesOSExe());
349
+ const reactNativePath = getReactNativePackagePath();
350
+ const bundledHermesEngine = path.join(reactNativePath, "sdks", "hermesc", getHermesOSBin(), getHermesOSExe());
188
351
  if (fileExists(bundledHermesEngine)) {
189
352
  return bundledHermesEngine;
190
353
  }
@@ -193,12 +356,13 @@ async function getHermesCommand(gradleFile) {
193
356
  return path.join("android", "app", gradleHermesCommand.replace("%OS-BIN%", getHermesOSBin()));
194
357
  }
195
358
  else {
359
+ const nodeModulesPath = getNodeModulesPath(reactNativePath);
196
360
  // assume if hermes-engine exists it should be used instead of hermesvm
197
- const hermesEngine = path.join("node_modules", "hermes-engine", getHermesOSBin(), getHermesOSExe());
361
+ const hermesEngine = path.join(nodeModulesPath, "hermes-engine", getHermesOSBin(), getHermesOSExe());
198
362
  if (fileExists(hermesEngine)) {
199
363
  return hermesEngine;
200
364
  }
201
- return path.join("node_modules", "hermesvm", getHermesOSBin(), "hermes");
365
+ return path.join(nodeModulesPath, "hermesvm", getHermesOSBin(), "hermes");
202
366
  }
203
367
  }
204
368
  function getComposeSourceMapsPath() {
@@ -209,6 +373,13 @@ function getComposeSourceMapsPath() {
209
373
  }
210
374
  return null;
211
375
  }
376
+ function getNodeModulesPath(reactNativePath) {
377
+ const nodeModulesPath = path.dirname(reactNativePath);
378
+ if (directoryExistsSync(nodeModulesPath)) {
379
+ return nodeModulesPath;
380
+ }
381
+ return path.join("node_modules");
382
+ }
212
383
  function getReactNativePackagePath() {
213
384
  const result = childProcess.spawnSync("node", ["--print", "require.resolve('react-native/package.json')"]);
214
385
  const packagePath = path.dirname(result.stdout.toString());
@@ -217,6 +388,7 @@ function getReactNativePackagePath() {
217
388
  }
218
389
  return path.join("node_modules", "react-native");
219
390
  }
391
+ exports.getReactNativePackagePath = getReactNativePackagePath;
220
392
  function directoryExistsSync(dirname) {
221
393
  try {
222
394
  return fs.statSync(dirname).isDirectory();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revopush/code-push-cli",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "Management CLI for the CodePush service",
5
5
  "main": "./script/cli.js",
6
6
  "scripts": {
@@ -26,6 +26,7 @@
26
26
  "backslash": "^0.2.0",
27
27
  "chalk": "^4.1.2",
28
28
  "cli-table": "^0.3.11",
29
+ "dotenv": "^17.2.1",
29
30
  "email-validator": "^2.0.4",
30
31
  "gradle-to-js": "2.0.1",
31
32
  "jsonwebtoken": "^9.0.2",