@bravemobile/react-native-code-push 12.0.2 → 12.1.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.
Files changed (100) hide show
  1. package/README.md +1 -1
  2. package/android/app/src/debug/AndroidManifest.xml +9 -0
  3. package/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java +1 -52
  4. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushNativeModule.java +4 -43
  5. package/bin/code-push.js +6 -0
  6. package/cli/commands/bundleCommand/{bundleCodePush.js → bundleCodePush.ts} +16 -24
  7. package/cli/commands/bundleCommand/{index.js → index.ts} +13 -14
  8. package/cli/commands/createHistoryCommand/{createReleaseHistory.js → createReleaseHistory.ts} +11 -28
  9. package/cli/commands/createHistoryCommand/{index.js → index.ts} +12 -13
  10. package/cli/commands/initCommand/{index.js → index.ts} +3 -3
  11. package/cli/commands/initCommand/{initAndroid.js → initAndroid.ts} +6 -11
  12. package/cli/commands/initCommand/initIos.ts +123 -0
  13. package/cli/commands/initCommand/test/initAndroid.test.ts +86 -0
  14. package/cli/commands/initCommand/test/initIos.test.ts +99 -0
  15. package/cli/commands/releaseCommand/{addToReleaseHistory.js → addToReleaseHistory.ts} +17 -45
  16. package/cli/commands/releaseCommand/{index.js → index.ts} +24 -25
  17. package/cli/commands/releaseCommand/release.ts +72 -0
  18. package/cli/commands/showHistoryCommand/{index.js → index.ts} +11 -12
  19. package/cli/commands/updateHistoryCommand/{index.js → index.ts} +17 -18
  20. package/cli/commands/updateHistoryCommand/{updateReleaseHistory.js → updateReleaseHistory.ts} +15 -41
  21. package/cli/constant.ts +4 -0
  22. package/cli/dist/commands/bundleCommand/bundleCodePush.js +34 -0
  23. package/cli/dist/commands/bundleCommand/index.js +14 -0
  24. package/cli/dist/commands/createHistoryCommand/createReleaseHistory.js +25 -0
  25. package/cli/dist/commands/createHistoryCommand/index.js +14 -0
  26. package/cli/dist/commands/initCommand/index.js +12 -0
  27. package/cli/dist/commands/initCommand/initAndroid.js +37 -0
  28. package/cli/{commands → dist/commands}/initCommand/initIos.js +13 -33
  29. package/cli/dist/commands/initCommand/test/initAndroid.test.js +75 -0
  30. package/cli/dist/commands/initCommand/test/initIos.test.js +95 -0
  31. package/cli/dist/commands/releaseCommand/addToReleaseHistory.js +32 -0
  32. package/cli/dist/commands/releaseCommand/index.js +38 -0
  33. package/cli/dist/commands/releaseCommand/release.js +36 -0
  34. package/cli/dist/commands/showHistoryCommand/index.js +14 -0
  35. package/cli/dist/commands/updateHistoryCommand/index.js +30 -0
  36. package/cli/dist/commands/updateHistoryCommand/updateReleaseHistory.js +26 -0
  37. package/cli/dist/constant.js +4 -0
  38. package/cli/dist/functions/getReactTempDir.js +10 -0
  39. package/cli/{functions → dist/functions}/makeCodePushBundle.js +5 -11
  40. package/cli/{functions → dist/functions}/prepareToBundleJS.js +2 -5
  41. package/cli/{functions → dist/functions}/runExpoBundleCommand.js +3 -21
  42. package/cli/{functions → dist/functions}/runHermesEmitBinaryCommand.js +12 -68
  43. package/cli/{functions → dist/functions}/runReactNativeBundleCommand.js +3 -23
  44. package/cli/dist/index.js +38 -0
  45. package/cli/dist/utils/file-utils.js +19 -0
  46. package/cli/dist/utils/fsUtils.js +37 -0
  47. package/cli/{utils → dist/utils}/hash-utils.js +19 -127
  48. package/cli/{utils → dist/utils}/promisfied-fs.js +5 -16
  49. package/cli/dist/utils/showLogo.js +21 -0
  50. package/cli/{utils → dist/utils}/zip.js +15 -51
  51. package/cli/functions/{getReactTempDir.js → getReactTempDir.ts} +2 -6
  52. package/cli/functions/makeCodePushBundle.ts +26 -0
  53. package/cli/functions/prepareToBundleJS.ts +10 -0
  54. package/cli/functions/runExpoBundleCommand.ts +45 -0
  55. package/cli/functions/runHermesEmitBinaryCommand.ts +186 -0
  56. package/cli/functions/runReactNativeBundleCommand.ts +51 -0
  57. package/cli/index.ts +48 -0
  58. package/cli/package.json +33 -0
  59. package/cli/utils/{file-utils.js → file-utils.ts} +4 -21
  60. package/cli/utils/{fsUtils.js → fsUtils.ts} +12 -19
  61. package/cli/utils/hash-utils.ts +146 -0
  62. package/cli/utils/promisfied-fs.ts +19 -0
  63. package/cli/utils/{showLogo.js → showLogo.ts} +1 -3
  64. package/cli/utils/zip.ts +65 -0
  65. package/package.json +42 -12
  66. package/{AlertAdapter.js → src/AlertAdapter.js} +5 -5
  67. package/AGENTS.md +0 -32
  68. package/CONTRIBUTING.md +0 -134
  69. package/SECURITY.md +0 -41
  70. package/android/app/src/main/java/com/microsoft/codepush/react/ReactInstanceHolder.java +0 -17
  71. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  72. package/android/gradle/wrapper/gradle-wrapper.properties +0 -5
  73. package/android/gradlew +0 -164
  74. package/android/gradlew.bat +0 -90
  75. package/app.plugin.js +0 -1
  76. package/babel.config.js +0 -3
  77. package/cli/commands/releaseCommand/release.js +0 -114
  78. package/cli/constant.js +0 -6
  79. package/cli/index.js +0 -49
  80. package/docs/api-android.md +0 -83
  81. package/docs/api-ios.md +0 -31
  82. package/docs/api-js.md +0 -592
  83. package/docs/multi-deployment-testing-android.md +0 -148
  84. package/docs/multi-deployment-testing-ios.md +0 -59
  85. package/eslint.config.mjs +0 -32
  86. package/scripts/generateBundledResourcesHash.js +0 -125
  87. package/scripts/getFilesInFolder.js +0 -19
  88. package/scripts/recordFilesBeforeBundleCommand.js +0 -41
  89. package/tsconfig.json +0 -18
  90. package/tslint.json +0 -32
  91. /package/{CodePush.js → src/CodePush.js} +0 -0
  92. /package/{logging.js → src/logging.js} +0 -0
  93. /package/{package-mixins.js → src/package-mixins.js} +0 -0
  94. /package/{versioning → src/versioning}/BaseVersioning.js +0 -0
  95. /package/{versioning → src/versioning}/BaseVersioning.test.js +0 -0
  96. /package/{versioning → src/versioning}/IncrementalVersioning.js +0 -0
  97. /package/{versioning → src/versioning}/IncrementalVersioning.test.js +0 -0
  98. /package/{versioning → src/versioning}/SemverVersioning.js +0 -0
  99. /package/{versioning → src/versioning}/SemverVersioning.test.js +0 -0
  100. /package/{versioning → src/versioning}/index.js +0 -0
@@ -0,0 +1,99 @@
1
+ import { modifyObjectiveCAppDelegate, modifySwiftAppDelegate } from "../initIos.js";
2
+ import { expect, describe, it} from "@jest/globals";
3
+
4
+ // https://github.com/react-native-community/template/blob/0.80.2/template/ios/HelloWorld/AppDelegate.swift
5
+ const swiftTemplate = `
6
+ import UIKit
7
+ import React
8
+ import React_RCTAppDelegate
9
+ import ReactAppDependencyProvider
10
+
11
+ @main
12
+ class AppDelegate: UIResponder, UIApplicationDelegate {
13
+ var window: UIWindow?
14
+ var reactNativeDelegate: ReactNativeDelegate?
15
+ var reactNativeFactory: RCTReactNativeFactory?
16
+
17
+ func application(
18
+ _ application: UIApplication,
19
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
20
+ ) -> Bool {
21
+ let delegate = ReactNativeDelegate()
22
+ let factory = RCTReactNativeFactory(delegate: delegate)
23
+ delegate.dependencyProvider = RCTAppDependencyProvider()
24
+ reactNativeDelegate = delegate
25
+ reactNativeFactory = factory
26
+ window = UIWindow(frame: UIScreen.main.bounds)
27
+ factory.startReactNative(
28
+ withModuleName: "HelloWorld",
29
+ in: window,
30
+ launchOptions: launchOptions
31
+ )
32
+ return true
33
+ }
34
+ }
35
+
36
+ class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate {
37
+ override func sourceURL(for bridge: RCTBridge) -> URL? {
38
+ self.bundleURL()
39
+ }
40
+
41
+ override func bundleURL() -> URL? {
42
+ #if DEBUG
43
+ RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
44
+ #else
45
+ Bundle.main.url(forResource: "main", withExtension: "jsbundle")
46
+ #endif
47
+ }
48
+ }
49
+ `;
50
+
51
+ // https://github.com/react-native-community/template/blob/0.76.9/template/ios/HelloWorld/AppDelegate.mm
52
+ const objcTemplate = `
53
+ #import "AppDelegate.h"
54
+
55
+ #import <React/RCTBundleURLProvider.h>
56
+
57
+ @implementation AppDelegate
58
+
59
+ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
60
+ {
61
+ self.moduleName = @"HelloWorld";
62
+ // You can add your custom initial props in the dictionary below.
63
+ // They will be passed down to the ViewController used by React Native.
64
+ self.initialProps = @{};
65
+
66
+ return [super application:application didFinishLaunchingWithOptions:launchOptions];
67
+ }
68
+
69
+ - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
70
+ {
71
+ return [self bundleURL];
72
+ }
73
+
74
+ - (NSURL *)bundleURL
75
+ {
76
+ #if DEBUG
77
+ return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
78
+ #else
79
+ return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
80
+ #endif
81
+ }
82
+
83
+ @end
84
+ `;
85
+
86
+ describe('iOS init command - pure functions', () => {
87
+ it('should correctly modify Swift AppDelegate content', () => {
88
+ const modifiedContent = modifySwiftAppDelegate(swiftTemplate);
89
+ expect(modifiedContent).toContain('CodePush.bundleURL()');
90
+ expect(modifiedContent).not.toContain('Bundle.main.url(forResource: "main", withExtension: "jsbundle")');
91
+ });
92
+
93
+ it('should correctly modify Objective-C AppDelegate content', () => {
94
+ const modifiedContent = modifyObjectiveCAppDelegate(objcTemplate);
95
+ expect(modifiedContent).toContain('#import <CodePush/CodePush.h>');
96
+ expect(modifiedContent).toContain('[CodePush bundleURL]');
97
+ expect(modifiedContent).not.toContain('[[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];');
98
+ });
99
+ });
@@ -1,46 +1,20 @@
1
- const path = require("path");
2
- const fs = require("fs");
3
-
4
- /**
5
- *
6
- * @param appVersion {string}
7
- * @param binaryVersion {string}
8
- * @param bundleDownloadUrl {string}
9
- * @param packageHash {string}
10
- * @param getReleaseHistory {
11
- * function(
12
- * targetBinaryVersion: string,
13
- * platform: string,
14
- * identifier?: string
15
- * ): Promise<ReleaseHistoryInterface>}
16
- * @param setReleaseHistory {
17
- * function(
18
- * targetBinaryVersion: string,
19
- * jsonFilePath: string,
20
- * releaseInfo: ReleaseHistoryInterface,
21
- * platform: string,
22
- * identifier?: string
23
- * ): Promise<void>}
24
- * @param platform {"ios" | "android"}
25
- * @param identifier {string?}
26
- * @param mandatory {boolean?}
27
- * @param enable {boolean?}
28
- * @param rollout {number?}
29
- * @returns {Promise<void>}
30
- */
31
- async function addToReleaseHistory(
32
- appVersion,
33
- binaryVersion,
34
- bundleDownloadUrl,
35
- packageHash,
36
- getReleaseHistory,
37
- setReleaseHistory,
38
- platform,
39
- identifier,
40
- mandatory,
41
- enable,
42
- rollout
43
- ) {
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import type { CliConfigInterface } from "../../../typings/react-native-code-push.d.ts";
4
+
5
+ export async function addToReleaseHistory(
6
+ appVersion: string,
7
+ binaryVersion: string,
8
+ bundleDownloadUrl: string,
9
+ packageHash: string,
10
+ getReleaseHistory: CliConfigInterface['getReleaseHistory'],
11
+ setReleaseHistory: CliConfigInterface['setReleaseHistory'],
12
+ platform: 'ios' | 'android',
13
+ identifier: string | undefined,
14
+ mandatory: boolean,
15
+ enable: boolean,
16
+ rollout: number | undefined,
17
+ ): Promise<void> {
44
18
  const releaseHistory = await getReleaseHistory(binaryVersion, platform, identifier);
45
19
 
46
20
  const updateInfo = releaseHistory[appVersion]
@@ -77,5 +51,3 @@ async function addToReleaseHistory(
77
51
  process.exit(1)
78
52
  }
79
53
  }
80
-
81
- module.exports = { addToReleaseHistory: addToReleaseHistory }
@@ -1,7 +1,25 @@
1
- const { program, Option } = require("commander");
2
- const { findAndReadConfigFile } = require("../../utils/fsUtils");
3
- const { release } = require("./release");
4
- const { OUTPUT_BUNDLE_DIR, CONFIG_FILE_NAME, ROOT_OUTPUT_DIR, ENTRY_FILE } = require('../../constant');
1
+ import { program, Option } from "commander";
2
+ import { findAndReadConfigFile } from "../../utils/fsUtils.js";
3
+ import { release } from "./release.js";
4
+ import { OUTPUT_BUNDLE_DIR, CONFIG_FILE_NAME, ROOT_OUTPUT_DIR, ENTRY_FILE } from "../../constant.js";
5
+
6
+ type Options = {
7
+ binaryVersion: string;
8
+ appVersion: string;
9
+ framework: 'expo' | undefined;
10
+ platform: 'ios' | 'android';
11
+ identifier?: string;
12
+ config: string;
13
+ outputPath: string;
14
+ entryFile: string;
15
+ bundleName: string;
16
+ mandatory: boolean;
17
+ enable: boolean;
18
+ rollout?: number;
19
+ skipBundle: boolean;
20
+ skipCleanup: boolean;
21
+ outputBundleDir: string;
22
+ }
5
23
 
6
24
  program.command('release')
7
25
  .description('Deploys a new CodePush update for a target binary app.\nAfter creating the CodePush bundle, it uploads the file and updates the ReleaseHistory information.\n`bundleUploader`, `getReleaseHistory`, and `setReleaseHistory` functions should be implemented in the config file.')
@@ -20,26 +38,7 @@ program.command('release')
20
38
  .option('--skip-bundle <bool>', 'skip bundle process', parseBoolean, false)
21
39
  .option('--skip-cleanup <bool>', 'skip cleanup process', parseBoolean, false)
22
40
  .option('--output-bundle-dir <string>', 'name of directory containing the bundle file created by the "bundle" command', OUTPUT_BUNDLE_DIR)
23
- /**
24
- * @param {Object} options
25
- * @param {string} options.binaryVersion
26
- * @param {string} options.appVersion
27
- * @param {string} options.framework
28
- * @param {string} options.platform
29
- * @param {string} options.identifier
30
- * @param {string} options.config
31
- * @param {string} options.outputPath
32
- * @param {string} options.entryFile
33
- * @param {string} options.bundleName
34
- * @param {string} options.mandatory
35
- * @param {string} options.enable
36
- * @param {number} options.rollout
37
- * @param {string} options.skipBundle
38
- * @param {string} options.skipCleanup
39
- * @param {string} options.outputBundleDir
40
- * @return {void}
41
- */
42
- .action(async (options) => {
41
+ .action(async (options: Options) => {
43
42
  const config = findAndReadConfigFile(process.cwd(), options.config);
44
43
 
45
44
  if (typeof options.rollout === 'number' && (options.rollout < 0 || options.rollout > 100)) {
@@ -70,7 +69,7 @@ program.command('release')
70
69
  console.log('🚀 Release completed.')
71
70
  });
72
71
 
73
- function parseBoolean(value) {
72
+ function parseBoolean(value: string): boolean | undefined {
74
73
  if (value === 'true') return true;
75
74
  if (value === 'false') return false;
76
75
  else return undefined;
@@ -0,0 +1,72 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { bundleCodePush } from "../bundleCommand/bundleCodePush.js";
4
+ import { addToReleaseHistory } from "./addToReleaseHistory.js";
5
+ import type { CliConfigInterface } from "../../../typings/react-native-code-push.d.ts";
6
+
7
+ export async function release(
8
+ bundleUploader: CliConfigInterface['bundleUploader'],
9
+ getReleaseHistory: CliConfigInterface['getReleaseHistory'],
10
+ setReleaseHistory: CliConfigInterface['setReleaseHistory'],
11
+ binaryVersion: string,
12
+ appVersion: string,
13
+ framework: 'expo' | undefined,
14
+ platform: 'ios' | 'android',
15
+ identifier: string | undefined,
16
+ outputPath: string,
17
+ entryFile: string,
18
+ jsBundleName: string,
19
+ mandatory: boolean,
20
+ enable: boolean,
21
+ rollout: number | undefined,
22
+ skipBundle: boolean,
23
+ skipCleanup: boolean,
24
+ bundleDirectory: string,
25
+ ): Promise<void> {
26
+ const bundleFileName = skipBundle
27
+ ? readBundleFileNameFrom(bundleDirectory)
28
+ : await bundleCodePush(framework, platform, outputPath, entryFile, jsBundleName, bundleDirectory);
29
+ const bundleFilePath = `${bundleDirectory}/${bundleFileName}`;
30
+
31
+ const downloadUrl = await (async () => {
32
+ try {
33
+ const { downloadUrl } = await bundleUploader(bundleFilePath, platform, identifier);
34
+ return downloadUrl
35
+ } catch (error) {
36
+ console.error('Failed to upload the bundle file. Exiting the program.\n', error)
37
+ process.exit(1)
38
+ }
39
+ })();
40
+
41
+ await addToReleaseHistory(
42
+ appVersion,
43
+ binaryVersion,
44
+ downloadUrl,
45
+ bundleFileName,
46
+ getReleaseHistory,
47
+ setReleaseHistory,
48
+ platform,
49
+ identifier,
50
+ mandatory,
51
+ enable,
52
+ rollout,
53
+ )
54
+
55
+ if (!skipCleanup) {
56
+ cleanUpOutputs(outputPath);
57
+ }
58
+ }
59
+
60
+ function cleanUpOutputs(dir: string) {
61
+ fs.rmSync(dir, { recursive: true });
62
+ }
63
+
64
+ function readBundleFileNameFrom(bundleDirectory: string): string {
65
+ const files = fs.readdirSync(bundleDirectory);
66
+ if (files.length !== 1) {
67
+ console.error('The bundlePath must contain only one file.');
68
+ process.exit(1);
69
+ }
70
+ const bundleFilePath = path.join(bundleDirectory, files[0]);
71
+ return path.basename(bundleFilePath);
72
+ }
@@ -1,6 +1,13 @@
1
- const { program, Option } = require("commander");
2
- const { findAndReadConfigFile } = require("../../utils/fsUtils");
3
- const { CONFIG_FILE_NAME } = require('../../constant');
1
+ import { program, Option } from "commander";
2
+ import { findAndReadConfigFile } from "../../utils/fsUtils.js";
3
+ import { CONFIG_FILE_NAME } from "../../constant.js";
4
+
5
+ type Options = {
6
+ binaryVersion: string;
7
+ platform: 'ios' | 'android';
8
+ identifier?: string;
9
+ config: string;
10
+ }
4
11
 
5
12
  program.command('show-history')
6
13
  .description('Retrieves and prints the release history of a specific binary version.\n`getReleaseHistory` function should be implemented in the config file.')
@@ -8,15 +15,7 @@ program.command('show-history')
8
15
  .addOption(new Option('-p, --platform <type>', 'platform').choices(['ios', 'android']).default('ios'))
9
16
  .option('-i, --identifier <string>', 'reserved characters to distinguish the release.')
10
17
  .option('-c, --config <path>', 'configuration file name (JS/TS)', CONFIG_FILE_NAME)
11
- /**
12
- * @param {Object} options
13
- * @param {string} options.binaryVersion
14
- * @param {string} options.platform
15
- * @param {string} options.identifier
16
- * @param {string} options.config
17
- * @return {void}
18
- */
19
- .action(async (options) => {
18
+ .action(async (options: Options) => {
20
19
  const config = findAndReadConfigFile(process.cwd(), options.config);
21
20
 
22
21
  const releaseHistory = await config.getReleaseHistory(
@@ -1,7 +1,18 @@
1
- const { program, Option } = require("commander");
2
- const { findAndReadConfigFile } = require("../../utils/fsUtils");
3
- const { updateReleaseHistory } = require("./updateReleaseHistory");
4
- const { CONFIG_FILE_NAME } = require('../../constant');
1
+ import { program, Option } from "commander";
2
+ import { findAndReadConfigFile } from "../../utils/fsUtils.js";
3
+ import { updateReleaseHistory } from "./updateReleaseHistory.js";
4
+ import { CONFIG_FILE_NAME } from "../../constant.js";
5
+
6
+ type Options = {
7
+ appVersion: string;
8
+ binaryVersion: string;
9
+ platform: 'ios' | 'android';
10
+ identifier?: string;
11
+ config: string;
12
+ mandatory?: boolean;
13
+ enable?: boolean;
14
+ rollout?: number;
15
+ }
5
16
 
6
17
  program.command('update-history')
7
18
  .description('Updates the release history for a specific binary version.\n`getReleaseHistory`, `setReleaseHistory` functions should be implemented in the config file.')
@@ -13,19 +24,7 @@ program.command('update-history')
13
24
  .option('-m, --mandatory <bool>', 'make the release to be mandatory', parseBoolean, undefined)
14
25
  .option('-e, --enable <bool>', 'make the release to be enabled', parseBoolean, undefined)
15
26
  .option('--rollout <number>', 'rollout percentage (0-100)', parseFloat, undefined)
16
- /**
17
- * @param {Object} options
18
- * @param {string} options.appVersion
19
- * @param {string} options.binaryVersion
20
- * @param {string} options.platform
21
- * @param {string} options.identifier
22
- * @param {string} options.config
23
- * @param {string} options.mandatory
24
- * @param {string} options.enable
25
- * @param {number} options.rollout
26
- * @return {void}
27
- */
28
- .action(async (options) => {
27
+ .action(async (options: Options) => {
29
28
  const config = findAndReadConfigFile(process.cwd(), options.config);
30
29
 
31
30
  if (typeof options.mandatory !== "boolean" && typeof options.enable !== "boolean") {
@@ -46,7 +45,7 @@ program.command('update-history')
46
45
  )
47
46
  });
48
47
 
49
- function parseBoolean(value) {
48
+ function parseBoolean(value: string) {
50
49
  if (value === 'true') return true;
51
50
  if (value === 'false') return false;
52
51
  else return undefined;
@@ -1,42 +1,18 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- /**
5
- *
6
- * @param appVersion {string}
7
- * @param binaryVersion {string}
8
- * @param getReleaseHistory {
9
- * function(
10
- * targetBinaryVersion: string,
11
- * platform: string,
12
- * identifier?: string
13
- * ): Promise<ReleaseHistoryInterface>}
14
- * @param setReleaseHistory {
15
- * function(
16
- * targetBinaryVersion: string,
17
- * jsonFilePath: string,
18
- * releaseInfo: ReleaseHistoryInterface,
19
- * platform: string,
20
- * identifier?: string
21
- * ): Promise<void>}
22
- * @param platform {"ios" | "android"}
23
- * @param identifier {string?}
24
- * @param mandatory {boolean?}
25
- * @param enable {boolean?}
26
- * @param rollout {number?}
27
- * @returns {Promise<void>}
28
- */
29
- async function updateReleaseHistory(
30
- appVersion,
31
- binaryVersion,
32
- getReleaseHistory,
33
- setReleaseHistory,
34
- platform,
35
- identifier,
36
- mandatory,
37
- enable,
38
- rollout
39
- ) {
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import type { CliConfigInterface } from "../../../typings/react-native-code-push.d.ts";
4
+
5
+ export async function updateReleaseHistory(
6
+ appVersion: string,
7
+ binaryVersion: string,
8
+ getReleaseHistory: CliConfigInterface['getReleaseHistory'],
9
+ setReleaseHistory: CliConfigInterface['setReleaseHistory'],
10
+ platform: 'ios' | 'android',
11
+ identifier: string | undefined,
12
+ mandatory: boolean | undefined,
13
+ enable: boolean | undefined,
14
+ rollout: number | undefined,
15
+ ): Promise<void> {
40
16
  const releaseHistory = await getReleaseHistory(binaryVersion, platform, identifier);
41
17
 
42
18
  const updateInfo = releaseHistory[appVersion]
@@ -61,5 +37,3 @@ async function updateReleaseHistory(
61
37
  process.exit(1)
62
38
  }
63
39
  }
64
-
65
- module.exports = { updateReleaseHistory: updateReleaseHistory }
@@ -0,0 +1,4 @@
1
+ export const CONFIG_FILE_NAME = "code-push.config.ts";
2
+ export const OUTPUT_BUNDLE_DIR = "bundleOutput";
3
+ export const ROOT_OUTPUT_DIR = "build";
4
+ export const ENTRY_FILE = "index.ts";
@@ -0,0 +1,34 @@
1
+ import fs from "fs";
2
+ import { prepareToBundleJS } from "../../functions/prepareToBundleJS.js";
3
+ import { runReactNativeBundleCommand } from "../../functions/runReactNativeBundleCommand.js";
4
+ import { runExpoBundleCommand } from "../../functions/runExpoBundleCommand.js";
5
+ import { getReactTempDir } from "../../functions/getReactTempDir.js";
6
+ import { runHermesEmitBinaryCommand } from "../../functions/runHermesEmitBinaryCommand.js";
7
+ import { makeCodePushBundle } from "../../functions/makeCodePushBundle.js";
8
+ import { ROOT_OUTPUT_DIR, ENTRY_FILE } from "../../constant.js";
9
+ /**
10
+ * @return {Promise<string>} CodePush bundle file name (equals to packageHash)
11
+ */
12
+ export async function bundleCodePush(framework, platform = 'ios', outputRootPath = ROOT_OUTPUT_DIR, entryFile = ENTRY_FILE, jsBundleName, // JS bundle file name (not CodePush bundle file)
13
+ bundleDirectory) {
14
+ if (fs.existsSync(outputRootPath)) {
15
+ fs.rmSync(outputRootPath, { recursive: true });
16
+ }
17
+ const OUTPUT_CONTENT_PATH = `${outputRootPath}/CodePush`;
18
+ const DEFAULT_JS_BUNDLE_NAME = platform === 'ios' ? 'main.jsbundle' : 'index.android.bundle';
19
+ const _jsBundleName = jsBundleName || DEFAULT_JS_BUNDLE_NAME; // react-native JS bundle output name
20
+ const SOURCEMAP_OUTPUT = `${outputRootPath}/${_jsBundleName}.map`;
21
+ prepareToBundleJS({ deleteDirs: [outputRootPath, getReactTempDir()], makeDir: OUTPUT_CONTENT_PATH });
22
+ if (framework === 'expo') {
23
+ runExpoBundleCommand(_jsBundleName, OUTPUT_CONTENT_PATH, platform, SOURCEMAP_OUTPUT, entryFile);
24
+ }
25
+ else {
26
+ runReactNativeBundleCommand(_jsBundleName, OUTPUT_CONTENT_PATH, platform, SOURCEMAP_OUTPUT, entryFile);
27
+ }
28
+ console.log('log: JS bundling complete');
29
+ await runHermesEmitBinaryCommand(_jsBundleName, OUTPUT_CONTENT_PATH, SOURCEMAP_OUTPUT);
30
+ console.log('log: Hermes compilation complete');
31
+ const { bundleFileName: codePushBundleFileName } = await makeCodePushBundle(OUTPUT_CONTENT_PATH, bundleDirectory);
32
+ console.log(`log: CodePush bundle created (file path: ./${bundleDirectory}/${codePushBundleFileName})`);
33
+ return codePushBundleFileName;
34
+ }
@@ -0,0 +1,14 @@
1
+ import { program, Option } from "commander";
2
+ import { bundleCodePush } from "./bundleCodePush.js";
3
+ import { OUTPUT_BUNDLE_DIR, ROOT_OUTPUT_DIR, ENTRY_FILE } from "../../constant.js";
4
+ program.command('bundle')
5
+ .description('Creates a CodePush bundle file (assumes Hermes is enabled).')
6
+ .addOption(new Option('-f, --framework <type>', 'framework type (expo)').choices(['expo']))
7
+ .addOption(new Option('-p, --platform <type>', 'platform').choices(['ios', 'android']).default('ios'))
8
+ .option('-o, --output-path <string>', 'path to output root directory', ROOT_OUTPUT_DIR)
9
+ .option('-e, --entry-file <string>', 'path to JS/TS entry file', ENTRY_FILE)
10
+ .option('-b, --bundle-name <string>', 'bundle file name (default-ios: "main.jsbundle" / default-android: "index.android.bundle")')
11
+ .option('--output-bundle-dir <string>', 'name of directory containing the bundle file created by the "bundle" command', OUTPUT_BUNDLE_DIR)
12
+ .action((options) => {
13
+ bundleCodePush(options.framework, options.platform, options.outputPath, options.entryFile, options.bundleName, `${options.outputPath}/${options.outputBundleDir}`);
14
+ });
@@ -0,0 +1,25 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ export async function createReleaseHistory(targetVersion, setReleaseHistory, platform, identifier) {
4
+ const BINARY_RELEASE = {
5
+ enabled: true,
6
+ mandatory: false,
7
+ downloadUrl: "",
8
+ packageHash: "",
9
+ };
10
+ const INITIAL_HISTORY = {
11
+ [targetVersion]: BINARY_RELEASE
12
+ };
13
+ try {
14
+ const JSON_FILE_NAME = `${targetVersion}.json`;
15
+ const JSON_FILE_PATH = path.resolve(process.cwd(), JSON_FILE_NAME);
16
+ console.log(`log: creating JSON file... ("${JSON_FILE_NAME}")\n`, JSON.stringify(INITIAL_HISTORY, null, 2));
17
+ fs.writeFileSync(JSON_FILE_PATH, JSON.stringify(INITIAL_HISTORY));
18
+ await setReleaseHistory(targetVersion, JSON_FILE_PATH, INITIAL_HISTORY, platform, identifier);
19
+ fs.unlinkSync(JSON_FILE_PATH);
20
+ }
21
+ catch (error) {
22
+ console.error('Error occurred while creating new history:', error);
23
+ process.exit(1);
24
+ }
25
+ }
@@ -0,0 +1,14 @@
1
+ import { program, Option } from "commander";
2
+ import { findAndReadConfigFile } from "../../utils/fsUtils.js";
3
+ import { createReleaseHistory } from "./createReleaseHistory.js";
4
+ import { CONFIG_FILE_NAME } from "../../constant.js";
5
+ program.command('create-history')
6
+ .description('Creates a new release history for the binary app.')
7
+ .requiredOption('-b, --binary-version <string>', '(Required) The target binary version')
8
+ .addOption(new Option('-p, --platform <type>', 'platform').choices(['ios', 'android']).default('ios'))
9
+ .option('-i, --identifier <string>', 'reserved characters to distinguish the release.')
10
+ .option('-c, --config <path>', 'set config file name (JS/TS)', CONFIG_FILE_NAME)
11
+ .action(async (options) => {
12
+ const config = findAndReadConfigFile(process.cwd(), options.config);
13
+ await createReleaseHistory(options.binaryVersion, config.setReleaseHistory, options.platform, options.identifier);
14
+ });
@@ -0,0 +1,12 @@
1
+ import { initAndroid } from "./initAndroid.js";
2
+ import { initIos } from "./initIos.js";
3
+ import { program } from "commander";
4
+ program
5
+ .command('init')
6
+ .description('Automatically performs iOS/Android native configurations to initialize the CodePush project.')
7
+ .action(async () => {
8
+ console.log('log: Start initializing CodePush...');
9
+ await initAndroid();
10
+ await initIos();
11
+ console.log('log: CodePush has been successfully initialized.');
12
+ });
@@ -0,0 +1,37 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import { EOL } from "os";
4
+ export function modifyMainApplicationKt(mainApplicationContent) {
5
+ if (mainApplicationContent.includes('CodePush.getJSBundleFile()')) {
6
+ console.log('log: MainApplication.kt already has CodePush initialized.');
7
+ return mainApplicationContent;
8
+ }
9
+ return mainApplicationContent
10
+ .replace('import com.facebook.react.ReactApplication', `import com.facebook.react.ReactApplication${EOL}import com.microsoft.codepush.react.CodePush`)
11
+ .replace('override fun getJSMainModuleName(): String = "index"', `override fun getJSMainModuleName(): String = "index"${EOL} override fun getJSBundleFile(): String = CodePush.getJSBundleFile()`);
12
+ }
13
+ export async function initAndroid() {
14
+ console.log('log: Running Android setup...');
15
+ await applyMainApplication();
16
+ }
17
+ async function applyMainApplication() {
18
+ const mainApplicationPath = await findMainApplication();
19
+ if (!mainApplicationPath) {
20
+ console.log('log: Could not find MainApplication.kt');
21
+ return;
22
+ }
23
+ if (mainApplicationPath.endsWith('.java')) {
24
+ console.log('log: MainApplication.java is not supported. Please migrate to MainApplication.kt.');
25
+ return;
26
+ }
27
+ const mainApplicationContent = fs.readFileSync(mainApplicationPath, 'utf-8');
28
+ const newContent = modifyMainApplicationKt(mainApplicationContent);
29
+ fs.writeFileSync(mainApplicationPath, newContent);
30
+ console.log('log: Successfully updated MainApplication.kt.');
31
+ }
32
+ async function findMainApplication() {
33
+ const searchPath = path.join(process.cwd(), 'android', 'app', 'src', 'main', 'java');
34
+ const files = fs.readdirSync(searchPath, { encoding: 'utf-8', recursive: true });
35
+ const mainApplicationFile = files.find(file => file.endsWith('MainApplication.java') || file.endsWith('MainApplication.kt'));
36
+ return mainApplicationFile ? path.join(searchPath, mainApplicationFile) : null;
37
+ }