@bravemobile/react-native-code-push 8.3.1 → 9.0.0-alpha.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/CodePush.js +107 -136
- package/README.md +0 -5
- package/babel-plugin-code-push/index.js +197 -0
- package/babel-plugin-code-push/package-lock.json +10463 -0
- package/babel-plugin-code-push/package.json +26 -0
- package/babel-plugin-code-push/test/.babelrc.js +12 -0
- package/babel-plugin-code-push/test/cases/test1-config +15 -0
- package/babel-plugin-code-push/test/cases/test1-input +3 -0
- package/babel-plugin-code-push/test/cases/test1-output +11 -0
- package/babel-plugin-code-push/test/cases/test2-config +9 -0
- package/babel-plugin-code-push/test/cases/test2-input +3 -0
- package/babel-plugin-code-push/test/cases/test2-output +7 -0
- package/babel-plugin-code-push/test/codepush.config.js +15 -0
- package/babel-plugin-code-push/test/plugin.test.js +44 -0
- package/babel.config.js +3 -0
- package/cli/commands/bundleCommand/bundleCodePush.js +49 -0
- package/cli/commands/bundleCommand/index.js +25 -0
- package/cli/commands/createHistoryCommand/createReleaseHistory.js +53 -0
- package/cli/commands/createHistoryCommand/index.js +28 -0
- package/cli/commands/releaseCommand/addToReleaseHistory.js +75 -0
- package/cli/commands/releaseCommand/index.js +61 -0
- package/cli/commands/releaseCommand/release.js +88 -0
- package/cli/commands/showHistoryCommand/index.js +28 -0
- package/cli/commands/updateHistoryCommand/index.js +49 -0
- package/cli/commands/updateHistoryCommand/updateReleaseHistory.js +62 -0
- package/cli/constant.js +3 -0
- package/cli/functions/getReactTempDir.js +16 -0
- package/cli/functions/makeCodePushBundle.js +27 -0
- package/cli/functions/prepareToBundleJS.js +12 -0
- package/cli/functions/runHermesEmitBinaryCommand.js +213 -0
- package/cli/functions/runReactNativeBundleCommand.js +59 -0
- package/cli/index.js +43 -0
- package/cli/utils/file-utils.js +42 -0
- package/cli/utils/fsUtils.js +49 -0
- package/cli/utils/hash-utils.js +227 -0
- package/cli/utils/promisfied-fs.js +29 -0
- package/cli/utils/showLogo.js +23 -0
- package/cli/utils/zip.js +89 -0
- package/code-push.config.example.supabase.ts +114 -0
- package/docs/setup-android.md +3 -11
- package/eslint.config.mjs +32 -0
- package/package-mixins.js +1 -8
- package/package.json +26 -23
- package/react-native.config.js +3 -1
- package/typings/react-native-code-push.d.ts +91 -16
- package/versioning/BaseVersioning.js +126 -0
- package/versioning/BaseVersioning.test.js +15 -0
- package/versioning/IncrementalVersioning.js +9 -0
- package/versioning/IncrementalVersioning.test.js +186 -0
- package/versioning/SemverVersioning.js +10 -0
- package/versioning/SemverVersioning.test.js +205 -0
- package/versioning/index.js +7 -0
- package/.azurepipelines/build-rn-code-push-1es.yml +0 -104
- package/.azurepipelines/test-rn-code-push.yml +0 -94
- package/.config/CredScanSuppressions.json +0 -14
- package/docs/setup-windows.md +0 -121
- package/request-fetch-adapter.js +0 -52
- package/scripts/postlink/android/postlink.js +0 -87
- package/scripts/postlink/ios/postlink.js +0 -116
- package/scripts/postlink/run.js +0 -11
- package/scripts/postunlink/android/postunlink.js +0 -74
- package/scripts/postunlink/ios/postunlink.js +0 -87
- package/scripts/postunlink/run.js +0 -11
- package/scripts/tools/linkToolsAndroid.js +0 -57
- package/scripts/tools/linkToolsIos.js +0 -130
package/CodePush.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { AcquisitionManager as Sdk } from "code-push/script/acquisition-sdk";
|
|
2
1
|
import { Alert } from "./AlertAdapter";
|
|
3
|
-
import requestFetchAdapter from "./request-fetch-adapter";
|
|
4
2
|
import { AppState, Platform } from "react-native";
|
|
5
3
|
import log from "./logging";
|
|
6
4
|
import hoistStatics from 'hoist-non-react-statics';
|
|
5
|
+
import { SemverVersioning } from './versioning/SemverVersioning'
|
|
7
6
|
|
|
8
7
|
let NativeCodePush = require("react-native").NativeModules.CodePush;
|
|
9
8
|
const PackageMixins = require("./package-mixins")(NativeCodePush);
|
|
@@ -27,7 +26,6 @@ async function checkForUpdate(deploymentKey = null, handleBinaryVersionMismatchC
|
|
|
27
26
|
* deployments (e.g. an early access deployment for insiders).
|
|
28
27
|
*/
|
|
29
28
|
const config = deploymentKey ? { ...nativeConfig, ...{ deploymentKey } } : nativeConfig;
|
|
30
|
-
const sdk = getPromisifiedSdk(requestFetchAdapter, config);
|
|
31
29
|
|
|
32
30
|
// Use dynamically overridden getCurrentPackage() during tests.
|
|
33
31
|
const localPackage = await module.exports.getCurrentPackage();
|
|
@@ -50,60 +48,102 @@ async function checkForUpdate(deploymentKey = null, handleBinaryVersionMismatchC
|
|
|
50
48
|
}
|
|
51
49
|
}
|
|
52
50
|
|
|
53
|
-
const update =
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
51
|
+
const update = await (async () => {
|
|
52
|
+
try {
|
|
53
|
+
// refer to `UpdateCheckRequest` type inside code-push SDK
|
|
54
|
+
const updateRequest = {
|
|
55
|
+
deployment_key: config.deploymentKey,
|
|
56
|
+
app_version: queryPackage.appVersion,
|
|
57
|
+
package_hash: queryPackage.packageHash,
|
|
58
|
+
is_companion: config.ignoreAppVersion,
|
|
59
|
+
label: queryPackage.label,
|
|
60
|
+
client_unique_id: config.clientUniqueId,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* `releaseHistory`
|
|
65
|
+
* @type {ReleaseHistoryInterface}
|
|
66
|
+
*/
|
|
67
|
+
const releaseHistory = await sharedCodePushOptions.releaseHistoryFetcher(updateRequest);
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* `runtimeVersion`
|
|
71
|
+
* The version of currently running CodePush update. (It can be undefined if the app is running without CodePush update.)
|
|
72
|
+
* @type {string|undefined}
|
|
73
|
+
*/
|
|
74
|
+
const runtimeVersion = updateRequest.label;
|
|
75
|
+
|
|
76
|
+
const versioning = new SemverVersioning(releaseHistory);
|
|
77
|
+
|
|
78
|
+
const shouldRollbackToBinary = versioning.shouldRollbackToBinary(runtimeVersion)
|
|
79
|
+
if (shouldRollbackToBinary) {
|
|
80
|
+
// Reset to latest major version and restart
|
|
81
|
+
CodePush.clearUpdates();
|
|
82
|
+
CodePush.allowRestart();
|
|
83
|
+
CodePush.restartApp();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const [latestVersion, latestReleaseInfo] = versioning.findLatestRelease();
|
|
87
|
+
const isMandatory = versioning.checkIsMandatory(runtimeVersion);
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Convert the update information decided from `ReleaseHistoryInterface` to be passed to the library core (original CodePush library).
|
|
91
|
+
*
|
|
92
|
+
* @type {UpdateCheckResponse} the interface required by the original CodePush library.
|
|
93
|
+
*/
|
|
94
|
+
const updateInfo = {
|
|
95
|
+
download_url: latestReleaseInfo.downloadUrl,
|
|
96
|
+
// (`enabled` will always be true in the release information obtained from the previous process.)
|
|
97
|
+
is_available: latestReleaseInfo.enabled,
|
|
98
|
+
package_hash: latestReleaseInfo.packageHash,
|
|
99
|
+
is_mandatory: isMandatory,
|
|
100
|
+
// 이건 항상 현재 실행중인 바이너리 버전을 전달한다.
|
|
101
|
+
// 조회한 업데이트가 현재 바이너리를 타겟하는가? 를 API 서버에서 판단한 다음, 해당 된다면 런타임 바이너리 버전을 그대로 돌려주던 것임.
|
|
102
|
+
// 우리는 updateChecker 조회 결과가 넘어왔다면 해당 정보는 현재 런타임 바이너리에 호환됨을 전제로 하고있음.
|
|
103
|
+
target_binary_range: updateRequest.app_version,
|
|
104
|
+
/**
|
|
105
|
+
* Retrieve the update version from the ReleaseHistory and store it in the label.
|
|
106
|
+
* This information can be accessed at runtime through the CodePush bundle metadata.
|
|
107
|
+
*/
|
|
108
|
+
label: latestVersion,
|
|
109
|
+
// false 전달해야 정상 동작함
|
|
110
|
+
update_app_version: false,
|
|
111
|
+
// 그닥 쓸모 없음
|
|
112
|
+
description: '',
|
|
113
|
+
// 런타임에 안쓰임
|
|
114
|
+
is_disabled: false,
|
|
115
|
+
// 런타임에 안쓰임
|
|
116
|
+
package_size: 0,
|
|
117
|
+
// 런타임에 안쓰임
|
|
118
|
+
should_run_binary_version: false,
|
|
119
|
+
}
|
|
77
120
|
|
|
78
|
-
// refer to `RemotePackage` type inside code-push SDK
|
|
79
|
-
return {
|
|
80
|
-
deploymentKey: config.deploymentKey,
|
|
81
|
-
description: updateInfo.description ?? '',
|
|
82
|
-
label: updateInfo.label ?? '',
|
|
83
|
-
appVersion: updateInfo.target_binary_range ?? '',
|
|
84
|
-
isMandatory: updateInfo.is_mandatory ?? false,
|
|
85
|
-
packageHash: updateInfo.package_hash ?? '',
|
|
86
|
-
packageSize: updateInfo.package_size ?? 0,
|
|
87
|
-
downloadUrl: updateInfo.download_url ?? '',
|
|
88
|
-
};
|
|
89
|
-
} catch (error) {
|
|
90
|
-
log(`An error has occurred at update checker : ${error.stack}`);
|
|
91
|
-
if (sharedCodePushOptions.fallbackToAppCenter) {
|
|
92
|
-
return await sdk.queryUpdateWithCurrentPackage(queryPackage);
|
|
93
|
-
} else {
|
|
94
|
-
// update will not happen
|
|
95
|
-
return undefined;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
})()
|
|
99
|
-
: await sdk.queryUpdateWithCurrentPackage(queryPackage);
|
|
100
121
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
122
|
+
if (!updateInfo) {
|
|
123
|
+
return null;
|
|
124
|
+
} else if (updateInfo.update_app_version) {
|
|
125
|
+
return { updateAppVersion: true, appVersion: updateInfo.target_binary_range };
|
|
126
|
+
} else if (!updateInfo.is_available) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// refer to `RemotePackage` type inside code-push SDK
|
|
131
|
+
return {
|
|
132
|
+
deploymentKey: config.deploymentKey,
|
|
133
|
+
description: updateInfo.description ?? '',
|
|
134
|
+
label: updateInfo.label ?? '',
|
|
135
|
+
appVersion: updateInfo.target_binary_range ?? '',
|
|
136
|
+
isMandatory: updateInfo.is_mandatory ?? false,
|
|
137
|
+
packageHash: updateInfo.package_hash ?? '',
|
|
138
|
+
packageSize: updateInfo.package_size ?? 0,
|
|
139
|
+
downloadUrl: updateInfo.download_url ?? '',
|
|
140
|
+
};
|
|
141
|
+
} catch (error) {
|
|
142
|
+
log(`An error has occurred at update checker :`, error);
|
|
143
|
+
// update will not happen
|
|
144
|
+
return undefined;
|
|
105
145
|
}
|
|
106
|
-
}
|
|
146
|
+
})();
|
|
107
147
|
|
|
108
148
|
/*
|
|
109
149
|
* There are four cases where checkForUpdate will resolve to null:
|
|
@@ -135,7 +175,7 @@ async function checkForUpdate(deploymentKey = null, handleBinaryVersionMismatchC
|
|
|
135
175
|
|
|
136
176
|
return null;
|
|
137
177
|
} else {
|
|
138
|
-
const remotePackage = { ...update, ...PackageMixins.remote(
|
|
178
|
+
const remotePackage = { ...update, ...PackageMixins.remote() };
|
|
139
179
|
remotePackage.failedInstall = await NativeCodePush.isFailedUpdate(remotePackage.packageHash);
|
|
140
180
|
remotePackage.deploymentKey = deploymentKey || nativeConfig.deploymentKey;
|
|
141
181
|
return remotePackage;
|
|
@@ -170,48 +210,6 @@ async function getUpdateMetadata(updateState) {
|
|
|
170
210
|
return updateMetadata;
|
|
171
211
|
}
|
|
172
212
|
|
|
173
|
-
function getPromisifiedSdk(requestFetchAdapter, config) {
|
|
174
|
-
// Use dynamically overridden AcquisitionSdk during tests.
|
|
175
|
-
const sdk = new module.exports.AcquisitionSdk(requestFetchAdapter, config);
|
|
176
|
-
sdk.queryUpdateWithCurrentPackage = (queryPackage) => {
|
|
177
|
-
return new Promise((resolve, reject) => {
|
|
178
|
-
module.exports.AcquisitionSdk.prototype.queryUpdateWithCurrentPackage.call(sdk, queryPackage, (err, update) => {
|
|
179
|
-
if (err) {
|
|
180
|
-
reject(err);
|
|
181
|
-
} else {
|
|
182
|
-
resolve(update);
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
sdk.reportStatusDeploy = (deployedPackage, status, previousLabelOrAppVersion, previousDeploymentKey) => {
|
|
189
|
-
return new Promise((resolve, reject) => {
|
|
190
|
-
module.exports.AcquisitionSdk.prototype.reportStatusDeploy.call(sdk, deployedPackage, status, previousLabelOrAppVersion, previousDeploymentKey, (err) => {
|
|
191
|
-
if (err) {
|
|
192
|
-
reject(err);
|
|
193
|
-
} else {
|
|
194
|
-
resolve();
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
sdk.reportStatusDownload = (downloadedPackage) => {
|
|
201
|
-
return new Promise((resolve, reject) => {
|
|
202
|
-
module.exports.AcquisitionSdk.prototype.reportStatusDownload.call(sdk, downloadedPackage, (err) => {
|
|
203
|
-
if (err) {
|
|
204
|
-
reject(err);
|
|
205
|
-
} else {
|
|
206
|
-
resolve();
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
return sdk;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
213
|
// This ensures that notifyApplicationReadyInternal is only called once
|
|
216
214
|
// in the lifetime of this module instance.
|
|
217
215
|
const notifyApplicationReady = (() => {
|
|
@@ -235,8 +233,7 @@ async function notifyApplicationReadyInternal() {
|
|
|
235
233
|
|
|
236
234
|
async function tryReportStatus(statusReport, retryOnAppResume) {
|
|
237
235
|
const config = await getConfiguration();
|
|
238
|
-
|
|
239
|
-
const previousDeploymentKey = statusReport.previousDeploymentKey || config.deploymentKey;
|
|
236
|
+
|
|
240
237
|
try {
|
|
241
238
|
if (statusReport.appVersion) {
|
|
242
239
|
log(`Reporting binary update (${statusReport.appVersion})`);
|
|
@@ -244,9 +241,6 @@ async function tryReportStatus(statusReport, retryOnAppResume) {
|
|
|
244
241
|
if (!config.deploymentKey) {
|
|
245
242
|
throw new Error("Deployment key is missed");
|
|
246
243
|
}
|
|
247
|
-
|
|
248
|
-
const sdk = getPromisifiedSdk(requestFetchAdapter, config);
|
|
249
|
-
await sdk.reportStatusDeploy(/* deployedPackage */ null, /* status */ null, previousLabelOrAppVersion, previousDeploymentKey);
|
|
250
244
|
} else {
|
|
251
245
|
const label = statusReport.package.label;
|
|
252
246
|
if (statusReport.status === "DeploymentSucceeded") {
|
|
@@ -255,10 +249,6 @@ async function tryReportStatus(statusReport, retryOnAppResume) {
|
|
|
255
249
|
log(`Reporting CodePush update rollback (${label})`);
|
|
256
250
|
await NativeCodePush.setLatestRollbackInfo(statusReport.package.packageHash);
|
|
257
251
|
}
|
|
258
|
-
|
|
259
|
-
config.deploymentKey = statusReport.package.deploymentKey;
|
|
260
|
-
const sdk = getPromisifiedSdk(requestFetchAdapter, config);
|
|
261
|
-
await sdk.reportStatusDeploy(statusReport.package, statusReport.status, previousLabelOrAppVersion, previousDeploymentKey);
|
|
262
252
|
}
|
|
263
253
|
|
|
264
254
|
NativeCodePush.recordStatusReported(statusReport);
|
|
@@ -346,7 +336,7 @@ function validateRollbackRetryOptions(rollbackRetryOptions) {
|
|
|
346
336
|
return true;
|
|
347
337
|
}
|
|
348
338
|
|
|
349
|
-
|
|
339
|
+
let testConfig;
|
|
350
340
|
|
|
351
341
|
// This function is only used for tests. Replaces the default SDK, configuration and native bridge
|
|
352
342
|
function setUpTestDependencies(testSdk, providedTestConfig, testNativeBridge) {
|
|
@@ -565,39 +555,23 @@ async function syncInternal(options = {}, syncStatusChangeCallback, downloadProg
|
|
|
565
555
|
let CodePush;
|
|
566
556
|
|
|
567
557
|
/**
|
|
568
|
-
* @callback
|
|
558
|
+
* @callback releaseHistoryFetcher
|
|
569
559
|
* @param {UpdateCheckRequest} updateRequest Current package information to check for updates.
|
|
570
|
-
* @returns {Promise<
|
|
560
|
+
* @returns {Promise<ReleaseHistoryInterface>} The release history of the updates deployed for a specific binary version.
|
|
571
561
|
*/
|
|
572
562
|
|
|
573
563
|
/**
|
|
574
564
|
* If you pass options once when calling `codePushify`, they will be shared with related functions.
|
|
575
565
|
* @type {{
|
|
576
|
-
*
|
|
577
|
-
*
|
|
578
|
-
* updateChecker: updateChecker | undefined,
|
|
579
|
-
* setUpdateChecker(updateCheckerFunction: updateChecker): void,
|
|
580
|
-
* fallbackToAppCenter: boolean,
|
|
581
|
-
* setFallbackToAppCenter(enable: boolean): void
|
|
566
|
+
* releaseHistoryFetcher: releaseHistoryFetcher | undefined,
|
|
567
|
+
* setReleaseHistoryFetcher(releaseHistoryFetcherFunction: releaseHistoryFetcher): void,
|
|
582
568
|
* }}
|
|
583
569
|
*/
|
|
584
570
|
const sharedCodePushOptions = {
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
if (
|
|
588
|
-
|
|
589
|
-
host += '/';
|
|
590
|
-
}
|
|
591
|
-
this.bundleHost = host;
|
|
592
|
-
},
|
|
593
|
-
updateChecker: undefined,
|
|
594
|
-
setUpdateChecker(updateCheckerFunction) {
|
|
595
|
-
if (updateCheckerFunction && typeof updateCheckerFunction !== 'function') throw new Error('pass a function to setUpdateChecker');
|
|
596
|
-
this.updateChecker = updateCheckerFunction;
|
|
597
|
-
},
|
|
598
|
-
fallbackToAppCenter: true,
|
|
599
|
-
setFallbackToAppCenter(enable) {
|
|
600
|
-
this.fallbackToAppCenter = enable;
|
|
571
|
+
releaseHistoryFetcher: undefined,
|
|
572
|
+
setReleaseHistoryFetcher(releaseHistoryFetcherFunction) {
|
|
573
|
+
if (!releaseHistoryFetcherFunction || typeof releaseHistoryFetcherFunction !== 'function') throw new Error('pass a function to releaseHistoryFetcher');
|
|
574
|
+
this.releaseHistoryFetcher = releaseHistoryFetcherFunction;
|
|
601
575
|
}
|
|
602
576
|
}
|
|
603
577
|
|
|
@@ -621,9 +595,7 @@ function codePushify(options = {}) {
|
|
|
621
595
|
);
|
|
622
596
|
}
|
|
623
597
|
|
|
624
|
-
sharedCodePushOptions.
|
|
625
|
-
sharedCodePushOptions.setUpdateChecker(options.updateChecker);
|
|
626
|
-
sharedCodePushOptions.setFallbackToAppCenter(options.fallbackToAppCenter);
|
|
598
|
+
sharedCodePushOptions.setReleaseHistoryFetcher(options.releaseHistoryFetcher);
|
|
627
599
|
|
|
628
600
|
const decorator = (RootComponent) => {
|
|
629
601
|
class CodePushComponent extends React.Component {
|
|
@@ -696,7 +668,6 @@ function codePushify(options = {}) {
|
|
|
696
668
|
if (NativeCodePush) {
|
|
697
669
|
CodePush = codePushify;
|
|
698
670
|
Object.assign(CodePush, {
|
|
699
|
-
AcquisitionSdk: Sdk,
|
|
700
671
|
checkForUpdate,
|
|
701
672
|
getConfiguration,
|
|
702
673
|
getCurrentPackage,
|
|
@@ -756,7 +727,7 @@ if (NativeCodePush) {
|
|
|
756
727
|
DEFAULT_ROLLBACK_RETRY_OPTIONS: {
|
|
757
728
|
delayInHours: 24,
|
|
758
729
|
maxRetryAttempts: 1
|
|
759
|
-
}
|
|
730
|
+
},
|
|
760
731
|
});
|
|
761
732
|
} else {
|
|
762
733
|
log("The CodePush module doesn't appear to be properly installed. Please double-check that everything is setup correctly.");
|
package/README.md
CHANGED
|
@@ -29,7 +29,6 @@ Specify a function to perform the update check using the `updateChecker` option.
|
|
|
29
29
|
|
|
30
30
|
(The `bundleHost` option can be used in combination.)
|
|
31
31
|
|
|
32
|
-
`fallbackToAppCenter` : If an error occurs during the execution of the updateChecker function, the original update check behavior is performed as a fallback. (default: true)
|
|
33
32
|
|
|
34
33
|
```javascript
|
|
35
34
|
const codePushOptions = {
|
|
@@ -41,7 +40,6 @@ const codePushOptions = {
|
|
|
41
40
|
});
|
|
42
41
|
return response;
|
|
43
42
|
},
|
|
44
|
-
fallbackToAppCenter: true,
|
|
45
43
|
};
|
|
46
44
|
|
|
47
45
|
export default codePush(codePushOptions)(MyApp);
|
|
@@ -72,7 +70,6 @@ This plugin provides client-side integration for the [CodePush service](https://
|
|
|
72
70
|
* [Getting Started](#getting-started)
|
|
73
71
|
* [iOS Setup](docs/setup-ios.md)
|
|
74
72
|
* [Android Setup](docs/setup-android.md)
|
|
75
|
-
* [Windows Setup](docs/setup-windows.md)
|
|
76
73
|
* [Plugin Usage](#plugin-usage)
|
|
77
74
|
* [Store Guideline Compliance](#store-guideline-compliance)
|
|
78
75
|
* [Releasing Updates](#releasing-updates)
|
|
@@ -105,7 +102,6 @@ In order to ensure that your end users always have a functioning version of your
|
|
|
105
102
|
|
|
106
103
|
- iOS (7+)
|
|
107
104
|
- Android (4.1+) on TLS 1.2 compatible devices
|
|
108
|
-
- Windows (UWP)
|
|
109
105
|
|
|
110
106
|
We try our best to maintain backwards compatibility of our plugin with previous versions of React Native, but due to the nature of the platform, and the existence of breaking changes between releases, it is possible that you need to use a specific version of the CodePush plugin in order to support the exact version of React Native you are using. The following table outlines which CodePush plugin versions officially support the respective React Native versions:
|
|
111
107
|
|
|
@@ -181,7 +177,6 @@ If you want to see how other projects have integrated with CodePush, you can che
|
|
|
181
177
|
Then continue with installing the native module
|
|
182
178
|
* [iOS Setup](docs/setup-ios.md)
|
|
183
179
|
* [Android Setup](docs/setup-android.md)
|
|
184
|
-
* [Windows Setup](docs/setup-windows.md)
|
|
185
180
|
|
|
186
181
|
|
|
187
182
|
## Plugin Usage
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const { parseExpression, parse } = require("@babel/parser");
|
|
4
|
+
|
|
5
|
+
const OPTIONS_TO_BUNDLE = [
|
|
6
|
+
"bundleHost",
|
|
7
|
+
"runtimeVersion",
|
|
8
|
+
"versioning",
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
module.exports = function (babel, options) {
|
|
12
|
+
const { types: t } = babel;
|
|
13
|
+
const configPath =
|
|
14
|
+
options.configPath != null
|
|
15
|
+
? path.resolve(options.configPath)
|
|
16
|
+
: path.resolve(process.cwd(), "code-push.config.js");
|
|
17
|
+
|
|
18
|
+
// Load config and imports from `code-push.config.js`
|
|
19
|
+
const { config, configImports, importedIdentifiers } = loadConfig(
|
|
20
|
+
babel,
|
|
21
|
+
configPath
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
visitor: {
|
|
26
|
+
Program(path) {
|
|
27
|
+
// Track imports in the input file to avoid duplicates
|
|
28
|
+
const existingImports = new Set();
|
|
29
|
+
path.traverse({
|
|
30
|
+
ImportDeclaration(importPath) {
|
|
31
|
+
existingImports.add(importPath.node.source.value);
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Add missing imports from code-push.config.js to the input file
|
|
36
|
+
configImports.forEach((importNode) => {
|
|
37
|
+
if (!existingImports.has(importNode.source.value)) {
|
|
38
|
+
// Clone the import node from code-push.config.js and add it to the input file
|
|
39
|
+
path.node.body.unshift(t.cloneNode(importNode));
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
ImportDeclaration(path, state) {
|
|
44
|
+
if (
|
|
45
|
+
path.node.source.value.includes("@bravemobile/react-native-code-push")
|
|
46
|
+
) {
|
|
47
|
+
const defaultImport = path.node.specifiers.find((specifier) =>
|
|
48
|
+
t.isImportDefaultSpecifier(specifier)
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// Save the imported name (e.g., "codePush") for later use
|
|
52
|
+
if (defaultImport) {
|
|
53
|
+
state.file.metadata.codePushImportName = defaultImport.local.name;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
CallExpression(path, state) {
|
|
58
|
+
const codePushImportName = state.file.metadata.codePushImportName;
|
|
59
|
+
if (!codePushImportName) return;
|
|
60
|
+
|
|
61
|
+
// Check if the current CallExpression is a call to the codePush function
|
|
62
|
+
if (t.isIdentifier(path.node.callee, { name: codePushImportName })) {
|
|
63
|
+
// Create an AST object representation of the configuration options to bundle
|
|
64
|
+
const configObjectExpression = t.objectExpression(
|
|
65
|
+
OPTIONS_TO_BUNDLE.map((key) =>
|
|
66
|
+
t.objectProperty(
|
|
67
|
+
t.identifier(key),
|
|
68
|
+
serializeConfigToNode(babel, importedIdentifiers, config[key])
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// Replace the arguments of codePush with the generated config object
|
|
74
|
+
path.node.arguments = [configObjectExpression];
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/** loads config file from configPath */
|
|
82
|
+
function loadConfig(babel, configPath) {
|
|
83
|
+
if (!fs.existsSync(configPath)) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
"code-push.config.js not found. Please ensure it exists in the root directory."
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const { types: t } = babel;
|
|
90
|
+
const configModule = require(configPath);
|
|
91
|
+
|
|
92
|
+
const configCode = fs.readFileSync(configPath, "utf8");
|
|
93
|
+
const ast = parse(configCode, {
|
|
94
|
+
sourceType: "module",
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Extract import declarations and track imported identifiers
|
|
98
|
+
const imports = [];
|
|
99
|
+
const importedIdentifiers = new Set();
|
|
100
|
+
|
|
101
|
+
const convertRequireIntoImportStatement = (declaration) => {
|
|
102
|
+
const moduleName = declaration.init.arguments[0].value;
|
|
103
|
+
if (t.isIdentifier(declaration.id)) {
|
|
104
|
+
// Case for `const fs = require("fs")`
|
|
105
|
+
return t.importDeclaration(
|
|
106
|
+
[t.importDefaultSpecifier(declaration.id)],
|
|
107
|
+
t.stringLiteral(moduleName)
|
|
108
|
+
);
|
|
109
|
+
} else if (t.isObjectPattern(declaration.id)) {
|
|
110
|
+
// Case for `const { parse } = require("module")`
|
|
111
|
+
const importSpecifiers = declaration.id.properties.map((property) =>
|
|
112
|
+
t.importSpecifier(property.value, property.key)
|
|
113
|
+
);
|
|
114
|
+
return t.importDeclaration(importSpecifiers, t.stringLiteral(moduleName));
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
ast.program.body.forEach((node) => {
|
|
119
|
+
if (t.isImportDeclaration(node)) {
|
|
120
|
+
// Handle import statements
|
|
121
|
+
imports.push(node);
|
|
122
|
+
node.specifiers.forEach((specifier) => {
|
|
123
|
+
importedIdentifiers.add(specifier.local.name);
|
|
124
|
+
});
|
|
125
|
+
} else if (t.isVariableDeclaration(node)) {
|
|
126
|
+
// Handle require function
|
|
127
|
+
node.declarations.forEach((declaration) => {
|
|
128
|
+
if (
|
|
129
|
+
t.isCallExpression(declaration.init) &&
|
|
130
|
+
t.isIdentifier(declaration.init.callee, { name: "require" }) &&
|
|
131
|
+
declaration.init.arguments.length === 1 &&
|
|
132
|
+
t.isStringLiteral(declaration.init.arguments[0])
|
|
133
|
+
) {
|
|
134
|
+
const importDeclaration =
|
|
135
|
+
convertRequireIntoImportStatement(declaration);
|
|
136
|
+
imports.push(importDeclaration);
|
|
137
|
+
declaration.id.properties.forEach((dec) => {
|
|
138
|
+
importedIdentifiers.add(dec.value.name); // Track the imported identifier
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
config: configModule.default || configModule,
|
|
147
|
+
configImports: imports,
|
|
148
|
+
importedIdentifiers,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Helper to serialize config values to AST nodes */
|
|
153
|
+
function serializeConfigToNode(babel, importedIdentifiers, value) {
|
|
154
|
+
const { types: t } = babel;
|
|
155
|
+
if (["string", "number", "boolean"].includes(typeof value) || value == null) {
|
|
156
|
+
return t.valueToNode(value); // Handle primitive values
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (Array.isArray(value)) {
|
|
160
|
+
return t.arrayExpression(
|
|
161
|
+
// Recursively handle arrays
|
|
162
|
+
value.map((v) => serializeConfigToNode(babel, importedIdentifiers, v))
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (typeof value === "object") {
|
|
167
|
+
return t.objectExpression(
|
|
168
|
+
Object.entries(value).map(([key, val]) =>
|
|
169
|
+
t.objectProperty(
|
|
170
|
+
t.identifier(key),
|
|
171
|
+
serializeConfigToNode(babel, importedIdentifiers, val)
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Use identifier for imported symbols instead of inlining
|
|
178
|
+
if (importedIdentifiers.has(value.name)) {
|
|
179
|
+
return t.identifier(value.name);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// For inline functions, parse and serialize them as expressions
|
|
183
|
+
if (typeof value === "function") {
|
|
184
|
+
const valueString = value.toString();
|
|
185
|
+
try {
|
|
186
|
+
return parseExpression(valueString, { sourceType: "module" });
|
|
187
|
+
} catch (error) {
|
|
188
|
+
throw new Error(
|
|
189
|
+
`Failed to parse function ${value.name || "anonymous"}: ${
|
|
190
|
+
error.message
|
|
191
|
+
}`
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
throw new Error(`Unsupported config value type: ${typeof value}`);
|
|
197
|
+
}
|